knitr::opts_chunk$set(echo = TRUE)

# Packages utilisés
library(config) # Utilisation d'un fichier de configuration, cf  https://db.rstudio.com/best-practices/managing-credentials/#stored-in-a-file-with-config
library(tidyverse)
library(DBI) # pour les connexions aux BDD
library(RPostgreSQL) # driver postgres
library(RMariaDB) # driver MariaDB
library(RMySQL) # driver MySQL
library(lubridate) # calcul sur les dates

library(httr) #
library(jsonlite) # Gestion format json

library("readxl") # Lecture de fichiers xlsx

library(sf) # Traitement de données géoréférencées
library(mapview) # Affichage de données géoréférencées
library(plotly) # Graphiques interactifs
library(leaflet) # Cartes interactives

# Fonction pour requêter l'API Hub'Eau

get_hubeau <- function(path, query) {
  
  response <- GET(url = path, query = query) %>% 
  content(as = "text", encoding = "UTF-8") %>%
  fromJSON(flatten = TRUE)
  
data = response$data # Le jeu de données est dans l'objet "data"

if(response$count > query$size) { # Si la taille de page est plus petite que le nb de résultats, faire une boucle pour les pages suivantes
  
pages <- ceiling(response$count/query$size)

# Affichage d'une barre de progression
pb <- winProgressBar(title = "Récupération des chroniques", min = 0,
                     max = pages, width = 300)

for(i in 2:pages){
  
  query$page <- i
  
  response_i <- GET(url = path, query = query) %>% 
  content(as = "text", encoding = "UTF-8") %>%
  fromJSON(flatten = TRUE)

data <- rbind(data, response_i$data) # Concaténation des lignes récupérées

setWinProgressBar(pb, i, title=paste( round(i/pages*100, 0), "% chargé")) # Avancement de la barre de progression

} # Fin boucle for

close(pb)

} # Fin if

return(data)
  } # Fin get_hubeau

# Insertion de données

db.insertion <- function(con,table,data) {
  
  # Date de la dernière donnée en base
  Date_max <- tbl(con, table) %>% summarise(Date_max = max(Date_de_la_mesure, na.rm = TRUE)) %>% pull(Date_max)
  if(is.na(Date_max)) Date_max <- as.Date(params$date_debut)-1
  
  # Données plus récentes à insérer
  data <- data %>% filter(Date_de_la_mesure > Date_max)
  
  # Existe-t-il des données à insérer ?
if (isTRUE(data %>% tally() > 0)) {
  
  # Insertion des nouvelles lignes
  dbWriteTable(con, table, data, overwrite=FALSE, append=TRUE,
             fileEncoding="latin1")
  
  # Horodate en commentaire
mise_a_jour <- paste0(format.Date(Sys.Date(),"%d/%m/%Y"), " : Actualisation ",params$actualisation)

dbGetQuery(con, paste0("ALTER TABLE ",table," COMMENT = '",mise_a_jour,"';"))

paste0("Données insérées : ",data%>%tally)

}
  else {
    
    paste0("Aucune donnée à insérer depuis le ", format.Date(Date_max,"%d/%m/%Y"))
  
    }
  
} # fin db.insertion

Connexion aux bases de données pour l’intégration des données importées


# Base Mariadb OEB - eau_tbi
con_eau_tbi <- dbConnect(RMariaDB::MariaDB(), default.file = '../../.my.cnf', groups="mysql_oeb",
dbname = "eau_tbi")

# Base MariaDB OEB - référentiels
con_referentiels <- dbConnect(RMariaDB::MariaDB(), default.file = '../../.my.cnf', groups="mysql_oeb",
dbname = "eau_referentiels")

# Base Postgres (développement)

conf <- config::get("postgres_dev")

con_postgresql_dev <- DBI::dbConnect(odbc::odbc(),
                          Driver       = conf$driver,
                          servername   = conf$server,
                          UID = conf$uid,
                          PWD = conf$pwd,
                          Port = conf$port,
                          database = 'eau',
                             encoding = "latin1")

Collecte des données de la base Naïades à partir de l’API Hydrobiologie du portail Hub’eau

Documentation de l’API : https://hubeau.eaufrance.fr/page/api-hydrobiologie


# Récupération des infos sur les stations de mesure

# Liste des paramètres de la requête
query_sites = list(
  code_departement= params$num_departement,
  format='json',
  size=params$pagination
  )

# Requête des stations
hubeau_sites <- get_hubeau(path = params$api_sites, query = query_sites)

# Conversion au format géographique
sites <- st_as_sf(hubeau_sites, coords = c("coordonnee_x", "coordonnee_y"), 
    crs = 2154, agr = "constant")

# Récupération des résultats d'indices biologiques

# Liste des paramètres de la requête (lot 1 de départements)
query_resultats = list(
                 #code_departement = params$num_departement,
                 code_departement = "29,22,35",
                 code_indice = params$codes_parametres,
                 #les paramètres date_debut et date_fin créent un bug dans l'API https://github.com/BRGM/hubeau/issues/81
                 #date_debut_prelevement = params$date_debut,
                 #date_fin_prelevement = params$date_fin,
                 size = params$pagination,
                 format = 'json')

# Requête des chroniques
hubeau_resultats <- get_hubeau(path = params$api_resultats, query=query_resultats)

# Liste des paramètres de la requête (lot 2 de départements)
query_resultats = list(
                 #code_departement = params$num_departement,
                 code_departement = "56,50,44,49,53",
                 code_indice = params$codes_parametres,
                 #les paramètres date_debut et date_fin créent un bug dans l'API https://github.com/BRGM/hubeau/issues/81
                 #date_debut_prelevement = params$date_debut,
                 #date_fin_prelevement = params$date_fin,
                 size = params$pagination,
                 format = 'json')

# Requête des chroniques
hubeau_resultats <- get_hubeau(path = params$api_resultats, query=query_resultats)%>%union(hubeau_resultats)

# Conversion au format géographique
resultats <- st_as_sf(hubeau_resultats, coords = c("coordonnee_x", "coordonnee_y"), 
    crs = 2154, agr = "constant")

Les stations de mesure et les chroniques d’analyses sont récupérées sur les départements 22,29,35,56,50,44,49,53 sur la période , via la fonction get_hubeau(path, query) définie ci-dessus.

Les départements séparés en deux lots pour respecter les contraintes de nb de lignes totales récupérées par requête.

Import des référentiels géographiques

Les polygones des territoires de SAGE bretons sont récupérées depuis le flux WFS de la DREAL Bretagne, ils correspondent au périmètre géographique des données à bancariser.

# couche des SAGEs bretons depuis Geobretagne
sages <- st_read("https://geobretagne.fr/geoserver/dreal_b/sage_dreal/wfs?SERVICE=WFS&REQUEST=GetCapabilities")
Reading layer `dreal_b:sage_dreal' from data source 
  `https://geobretagne.fr/geoserver/dreal_b/sage_dreal/wfs?SERVICE=WFS&REQUEST=GetCapabilities' using driver `WFS'
Simple feature collection with 21 features and 7 fields
Geometry type: MULTISURFACE
Dimension:     XY
Bounding box:  xmin: 123903.4 ymin: 6703536 xmax: 422117.4 ymax: 6882053
Projected CRS: RGF93 / Lambert-93
# pb pas moyen de manipuler cet objet => solution sur
# https://gis.stackexchange.com/questions/389814/r-st-centroid-geos-error-unknown-wkb-type-12/389854#389854
ensure_multipolygons <- function(X) {
  tmp1 <- tempfile(fileext = ".gpkg")
  tmp2 <- tempfile(fileext = ".gpkg")
  st_write(X, tmp1)
  gdalUtilities::ogr2ogr(tmp1, tmp2, f = "GPKG", nlt = "MULTIPOLYGON")
  Y <- st_read(tmp2)
  st_sf(st_drop_geometry(X), geom = st_geometry(Y))
}

sages <- ensure_multipolygons(sages)
Writing layer `file1678231e7beb' to data source `C:\Users\tbesse\AppData\Local\Temp\RtmpYrWJEH\file1678231e7beb.gpkg' using driver `GPKG'
Writing 21 features with 7 fields and geometry type Multi Surface.
Reading layer `file1678231e7beb' from data source `C:\Users\tbesse\AppData\Local\Temp\RtmpYrWJEH\file1678334e4c1c.gpkg' using driver `GPKG'
Simple feature collection with 21 features and 7 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 123903.4 ymin: 6703536 xmax: 422117.4 ymax: 6882053
Projected CRS: RGF93 / Lambert-93
# couche des Hydroécorégions de niveau 2

her2 <- st_read("https://services.sandre.eaufrance.fr/geo/mdo?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&typename=Hydroecoregion2")%>%
  st_transform(2154)
Reading layer `Hydroecoregion2' from data source 
  `https://services.sandre.eaufrance.fr/geo/mdo?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&typename=Hydroecoregion2' 
  using driver `GML'
Simple feature collection with 114 features and 5 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -4.79495 ymin: 41.36882 xmax: 9.560037 ymax: 51.08965
Geodetic CRS:  WGS 84

Sélection des sites et des résultats sur les territoires des SAGE bretons

Les sites et résultats téléchargés sont sélectionnés sur le périmètre géographique retenu, par jointure avec la couche des SAGE.


sites_sages <- sites %>% 
  st_join(sages) %>% 
  filter(!is.na(cd_sage))

resultats_sages <- resultats %>% 
  st_join(sages) %>% 
  filter(!is.na(cd_sage)) %>% 
  st_join(her2)

Les sites font-ils partie du réseau RCS ?

L’information sur le réseau de suivi est incluse dans les données sur les sites fournies par l’API. Le réseau RCS correspond au code SANDRE : 0000000052

sites_rcs <- sites_sages %>%
  # rowwise pour préciser que les opérations se font pour chaque ligne
  rowwise()%>%
  # Le code 0000000052 (RCS) est dans la liste des codes_réseaux
  mutate(inclus_rcs = '0000000052' %in% unlist(codes_reseaux))%>%
  select(code_station_hydrobio, inclus_rcs)

Carte des sites importés via Hub’eau, par département

sites_sages %>%
  ggplot()+
  geom_sf(data=sages)+
geom_sf(aes(color=libelle_departement))

Carte des résultats importés via Hub’eau, par qualification

resultats_sages %>%
  ggplot()+
  geom_sf(data=sages)+
geom_sf(aes(color=libelle_qualification))

Exemple de résulat : Carte de l’indice Diatomées (IBD)

resultats_sages %>%
  filter(code_indice == '5856')%>% #5856 IBD Indice Diatomées
  ggplot()+
  geom_sf(data=sages)+
geom_sf(aes(color=resultat_indice))

Jointure avec les tables référentiels

Sites inconnus

Les sites importés sont comparés à la table des sites bancarisés précédemment, par code SANDRE.

Liste des sites inconnus :

sites_inconnus <- sites_sages %>%
  # tables des sites
  left_join(tbl(con_postgresql_dev, dbplyr::in_schema("eau_structure","site")), by=c("code_station_hydrobio" = "code"), copy=TRUE, suffix = c("", ".site"))%>%
  filter(is.na(site_id))

sites_inconnus %>% 
  distinct(code_station_hydrobio, libelle_station_hydrobio)

Carte des sites inconnus :

sites_inconnus %>%
  ggplot()+
  geom_sf(data=sages)+
geom_sf(aes(color=libelle_departement))

Mise en forme des nouveaux sites en fonction du schema de la table des sites bancarisés


insert_sites <- sites_inconnus %>%
  mutate(coord_x = st_coordinates(st_transform(geometry, 2154))[,1],
         coord_y = st_coordinates(st_transform(geometry, 2154))[,2],
         longitude_wgs84 = st_coordinates(st_transform(geometry, 4326))[,1],
         latitude_wgs84 = st_coordinates(st_transform(geometry, 4326))[,2],
         typesite_id = '1',
         projection_id = '2154',
         source = 'OFB/NAIADES',
         maj = format(Sys.Date(),"%Y-%m-%d")
         )%>% 
  as_tibble() %>%
select(code = code_station_hydrobio,
libelle = libelle_station_hydrobio,
typesite_id,
coord_x,
coord_y,
projection_id,
longitude_wgs84,
latitude_wgs84,
source,
maj)

insert_sites
NA

Insertion des nouveaux sites

Table de correspondance site / territoires (EGA - Entités Geographiques et Administratives)

La table de correspondance est issue de la jointure spatiale entre la table des sites et les différentes échelles de territoires bretons : Région, Départements, EPCI, SAGE, Contrats de territoires

correspondance_site_ega <- tbl(con_postgresql_dev, dbplyr::in_schema("eau_referentiel","geo_correspondance_site_ega"))%>%
  mutate(typesite = 'SITE')

correspondance_site_ega

Liste des paramètres bancarisés

La liste des paramètres est issue du dictionnaire national des données sur l’eau (SANDRE).


liste_parametres <- as.list(strsplit(params$codes_parametres, ",")[[1]])

parametres <- tbl(con_postgresql_dev, dbplyr::in_schema("eau_structure","parametre"))%>%
  collect() %>%
  filter(code %in% liste_parametres)

parametres

Seuils de qualité DCE

La liste des seuils de qualité est produite à partir de l’Arrêté du 27 juillet 2018 disponible sur Legifrance[^footnote]

[^footnote] Arrêté du 27 juillet 2018 modifiant l’arrêté du 25 janvier 2010 relatif aux méthodes et critères d’évaluation de l’état écologique, de l’état chimique et du potentiel écologique des eaux de surface pris en application des articles R. 212-10, R. 212-11 et R. 212-18 du code de l’environnement - Légifrance (legifrance.gouv.fr)

Table complète

Table des résultats avec les attributs des référentiels

table_indices <- resultats_sages %>%
  mutate(CoordX_WGS84 = st_coordinates(st_transform(geometry, 4326))[,1],
         CoordY_WGS84 = st_coordinates(st_transform(geometry, 4326))[,2],
         date_prelevement = as.Date(date_prelevement)
         )%>%
  as_tibble()%>%
  # tables des paramètres
  left_join(parametres, by=c("code_indice"="code"), suffix = c("", ".parametre"))%>%
  # Unité inconnue --> code sandre 0
  # n (nombre) --> code sandre 214
  # ‰ vs SMOW --> code sandre 32
  mutate(unite_code = case_when(unite_indice == "Unité inconnue" ~ '0',
                                  unite_indice == "n" ~ '214',
                                  unite_indice == "‰ vs SMOW" ~ '32',
                                  TRUE ~ unite_indice),
         resultat_indice = ifelse(resultat_indice == 999, NA, resultat_indice)) %>%
  # tables des unités
  left_join(tbl(con_postgresql_dev, dbplyr::in_schema("eau_referentiel","unite")), by=c("unite_code" = "code"), copy=TRUE, suffix = c("", ".unite"))%>%
  # tables des sites
  left_join(tbl(con_postgresql_dev, dbplyr::in_schema("eau_structure","site")), by=c("code_station_hydrobio" = "code"), copy=TRUE, suffix = c("", ".site"))

table_indices 

Synthèse des données manquantes

# Synthèse des données manquantes
table_indices %>%
  summarise(
    nb_lignes = n(),
    lignes_sans_parametre = sum(is.na(parametre_id)),
    lignes_sans_unite = sum(is.na(unite_id)),
    lignes_sans_sites = sum(is.na(site_id))
  )

# Unités inconnues de la table de référence
table_indices %>% filter(is.na(unite_id)) %>% distinct(unite_indice)

# Stations inconnues de la table des sites
table_indices %>% filter(is.na(site_id)) %>% distinct(libelle_station_hydrobio)

# Liste des codes indices
table_indices %>% distinct(code_indice)
NA

Classes des indices

Attribution des classes de qualité aux résultats par paramètre (indice biologique)

table_indices_classes <- table_indices %>% 
  left_join(classes_qualite, by = c("parametre_id"), copy = TRUE, suffix=c("",".classe")) %>%
  filter(resultat_indice < borne_sup_exclue & resultat_indice >= borne_inf_inclue)

table_indices_classes

Exploration des données

Données disponibles par an et par indice

table_indices_classes %>%
  group_by(year(date_prelevement), libelle_support, libelle_indice) %>%
  summarise(Nb_resultats = n(),
            Annee = year(date_prelevement)) %>%
  ggplot(aes(x = as.factor(Annee), y=Nb_resultats, fill=libelle_indice))+
           geom_bar(stat="identity")+
  facet_grid(libelle_support~.)
`summarise()` has grouped output by 'year(date_prelevement)', 'libelle_support', 'libelle_indice'. You can override using the `.groups` argument.

NA

Distribution des résultats par indice et par classe de qualité

table_indices_classes %>%
  arrange(code.x)%>%
  ggplot(aes(x = code.x, y=resultat_indice, fill = code.x))+
           geom_boxplot()+
  facet_wrap(~libelle_indice, scales = "free")

  
table_indices_classes

Transformation des données

Données annuelles

Données aggrégées par année d’analyse :

  • Indices moyens
  • Classes maximales

table_indices_classes_annee <- table_indices_classes %>%
  mutate(Annee = year(as.Date(date_prelevement))) %>%
  group_by(code_station_hydrobio,
           longitude_wgs84,
           latitude_wgs84,
           libelle_support,
           code_indice,
           parametre_id,
           libelle,
           symbole,
           Annee) %>%
  summarise(classe = as.integer(max(code.x)), 
            resultat_indice = mean(resultat_indice),
            resultat_qualification = as.integer(max(code_qualification)))
`summarise()` has grouped output by 'code_station_hydrobio', 'longitude_wgs84', 'latitude_wgs84', 'libelle_support', 'code_indice', 'parametre_id', 'libelle', 'symbole'. You can override using the `.groups` argument.
table_indices_classes_annee

Classe de qualité biologique globale

Calcul d’une classe de qualité biologique globale (maximum des classes des indices annuels)

table_indices_classe_globale_annee <- table_indices_classes_annee %>%
  group_by(code_station_hydrobio, longitude_wgs84, latitude_wgs84, Annee)%>%
  summarise(Resultat = max(classe))%>%
  mutate(Serie = 'Classe - Qualité biologique Globale', 
         libelle_support = 'Qualité biologique Globale',
         symbole = 'X')
`summarise()` has grouped output by 'code_station_hydrobio', 'longitude_wgs84', 'latitude_wgs84'. You can override using the `.groups` argument.
table_indices_classe_globale_annee

Import des données en base

Données au format de la table eau_structure.analyse_bio_esu - serveur PostgreSQL DEV

Mise en forme de la table


analyse_bio_esu <- table_indices %>%
  filter(!is.na(resultat_indice))%>%
  left_join(select(tbl(con_postgresql_dev, dbplyr::in_schema("eau_structure","date")), date_id, date_du_jour), by=c("date_prelevement" = "date_du_jour"), copy=TRUE)%>%
  left_join(select(tbl(con_postgresql_dev, dbplyr::in_schema("eau_structure","support")), support_id, code), by=c("code_support" = "code"), copy=TRUE)%>%
  left_join(select(tbl(con_postgresql_dev, dbplyr::in_schema("eau_structure","remarque")), remarque_id, code), by=c("code_qualification" = "code"), copy=TRUE)%>%
  mutate(prelevement_code = paste0(code_station_hydrobio,date_id),
         rdd_id = 0,
         milieu_id = 3,
         fraction_id = 22,
         limite_quantification = 0,
         source = 'OFB/NAIADES',
         maj = format(Sys.Date(),"%Y-%m-%d"))%>%
  select(site_id,
         date_id,
         rdd_id,
         prelevement_code,
         milieu_id,
         support_id,
         fraction_id,
         parametre_id,
         resultat = resultat_indice,
         remarque_id,
         limite_quantification,
         source,
         maj)

analyse_bio_esu

Sélection des nouvelles lignes à insérer


insert_analyse_bio_esu <- analyse_bio_esu

Insertion des nouvelles lignes

Données au format de la table eau_tbi.oeb_eau_qualite_biologique_ce - serveur MariaDB OEB


table_series_indices <- table_indices_classes_annee %>% 
  mutate(indice = paste(libelle, code_indice, sep=' - '))%>%
  pivot_longer(cols= c("classe","resultat_indice","resultat_qualification"))%>%
  mutate(Serie = case_when(name == 'classe' ~ paste('Classe',libelle_support,sep = ' - '),
                           name == 'resultat_indice' ~ paste('Indice',indice,sep = ' - '),
                           name == 'resultat_qualification' ~ paste('Qualification',indice,sep = ' - ')
                           )
         )%>%
  group_by(code_station_hydrobio,
           longitude_wgs84,
           latitude_wgs84,
           libelle_support,
           Serie,
           symbole,
           Annee)%>%
  summarise(Resultat = max(value))%>%
  ungroup()%>%
  union(table_indices_classe_globale_annee)
`summarise()` has grouped output by 'code_station_hydrobio', 'longitude_wgs84', 'latitude_wgs84', 'libelle_support', 'Serie', 'symbole'. You can override using the `.groups` argument.
table_series_indices

Déclinaison par combinaison SITE / EGA

La table de valorisation décline les résultats pour chaque échelle de territoire

table_series_indices_ega <- table_series_indices%>%
  # table des correspondances sites / UGA
  left_join(correspondance_site_ega, by=c("code_station_hydrobio" = "cdsite"), copy = TRUE)%>%
  left_join(sites_rcs, by="code_station_hydrobio")

table_series_indices_ega

Mise en forme de la table


oeb_eau_qualite_biologique_ce <- table_series_indices_ega %>%
  mutate(Periode = as.character(Annee),
         Source = 'OFB/NAIADES',
         Mise_a_jour = format(Sys.Date(),"%Y-%m-%d")
         )%>% 
  select(Type_entitite_geographique = typesite,
         Code_entitite_geographique = code_station_hydrobio,
         Libelle_entitite_geographique = lbsite,
         CoordX_WGS84 = longitude_wgs84,
         CoordY_WGS84 = latitude_wgs84,
         Reseau_RCS = inclus_rcs,
         Type_entitite_geographique_associee = typeega,
         Code_entitite_geographique_associee = cdega,
         Libelle_entitite_geographique_associee = lbega,
         Periode,
         Serie,
         unite = symbole,
         Resultat,
         Source,
         Mise_a_jour
  )

oeb_eau_qualite_biologique_ce
NA

Sélection des lignes nouvelles à insérer

La table de résultats est comparée aux données déjà bancarisées par une jointure d’exclusion (anti_join). Les lignes restantes peuvent être insérées.


insert_oeb_eau_qualite_biologique_ce <- oeb_eau_qualite_biologique_ce %>% 
  ungroup() %>%
  # Retirer les lignes des résultats déjà existants dans la table
  anti_join(tbl(con_eau_tbi,"oeb_eau_qualite_biologique_ce", by=c("Type_entitite_geographique", "Code_entitite_geographique", "Type_entitite_geographique_associee", "Code_entitite_geographique_associee", "Periode", "Serie")), copy = TRUE)%>%
  # Retirer les stations hydro sans EGA identifiée
  filter(!is.na(Type_entitite_geographique))%>%
  # Retirer les lignes avec une valeur NA
  drop_na()
Joining, by = c("Type_entitite_geographique", "Code_entitite_geographique", "Libelle_entitite_geographique", "CoordX_WGS84", "CoordY_WGS84", "Reseau_RCS", "Type_entitite_geographique_associee", "Code_entitite_geographique_associee", "Libelle_entitite_geographique_associee", "Periode", "Serie", "unite", "Resultat", "Source", "Mise_a_jour")
insert_oeb_eau_qualite_biologique_ce

Insertion des nouvelles lignes

Exports CSV pour la publication du jeu de données sur les portails opendata

Export pour le GIDE

Fichier pour la publication du jeu de données sur le portail OEB

# Depuis la base de données
#tbl(con_eau_tbi, "oeb_eau_qualite_biologique_ce_new")%>%
# ou depuis la table en mémoire
oeb_eau_qualite_biologique_ce %>%  
write.table(file = paste0(params$path_dataviz,"\\GIDE\\","oeb_eau_qualite_biologique_ce.csv"), quote = TRUE, sep = ";",
            eol = "\n", na = "", dec = ",",
            fileEncoding = "UTF-8")

Export pour GEOB

Fichier pour la publication du jeu de données sur Geobretagne

Mise en forme de la table pour l’ensemble des paramètres

oeb_eau_qualite_geob <- table_series_indices  %>%
  ungroup()%>%
  left_join(select(sites_sages,code_station_hydrobio,libelle_station_hydrobio), by="code_station_hydrobio")  %>%
  mutate(Code_entitite_geographique = code_station_hydrobio,
         Libelle_entitite_geographique = libelle_station_hydrobio,
         CoordX_WGS84 = longitude_wgs84,
         CoordY_WGS84 = latitude_wgs84,
         Serie,
         unite = symbole,
         Type_entitite_geographique = 'SITE',
         Source = 'OFB/NAIADES',
         Mise_a_jour = format(Sys.Date(),"%Y-%m-%d")) %>%
  select(Type_entitite_geographique,
         Code_entitite_geographique,
         Libelle_entitite_geographique,
         CoordX_WGS84,
         CoordY_WGS84,
         Serie,
         unite,
         Source,
         Mise_a_jour,
         Annee,
         Resultat) %>%
  pivot_wider(values_from = Resultat, names_from = Annee, names_sort=TRUE)

Export pour le paramètre “Qualité globale”


oeb_eau_qualite_geob  %>%
  filter(Serie == 'Classe - Qualité biologique Globale')  %>%
write.table(file = paste0(params$path_dataviz,"\\GEOB\\","oeb_eau_qualite_biologique_globale.csv"), quote = TRUE, sep = ";",
            eol = "\n", na = "", dec = ",",
            fileEncoding = "UTF-8")

Export pour le paramètre “Diatomées”


oeb_eau_qualite_geob %>%
  filter(Serie == 'Classe - Diatomées benthiques') %>%
write.table(file = paste0(params$path_dataviz,"\\GEOB\\","oeb_eau_qualite_diatomees.csv"), quote = TRUE, sep = ";",
            eol = "\n", na = "", dec = ",",
            fileEncoding = "UTF-8")

Export pour le paramètre “Macroinvertébrés”


oeb_eau_qualite_geob %>%
  filter(Serie == 'Classe - Macroinvertébrés aquatiques') %>%
write.table(file = paste0(params$path_dataviz,"\\GEOB\\","oeb_eau_qualite_macroinvertebres.csv"), quote = TRUE, sep = ";",
            eol = "\n", na = "", dec = ",",
            fileEncoding = "UTF-8")

Export pour le paramètre “Macrophytes”


oeb_eau_qualite_geob %>%
  filter(Serie == 'Classe - Macrophytes') %>%
write.table(file = paste0(params$path_dataviz,"\\GEOB\\","oeb_eau_qualite_macrophytes.csv"), quote = TRUE, sep = ";",
            eol = "\n", na = "", dec = ",",
            fileEncoding = "UTF-8")
LS0tDQp0aXRsZTogIkludGVncmF0aW9uIGRlcyBkb25uw6llcyBoeWRyb2Jpb2xvZ2lxdWVzIg0Kb3V0cHV0OiBodG1sX2RvY3VtZW50DQpwYXJhbXM6DQogIGFwaV9zaXRlczogaHR0cHM6Ly9odWJlYXUuZWF1ZnJhbmNlLmZyL2FwaS92YmV0YS9oeWRyb2Jpby9zdGF0aW9uc19oeWRyb2Jpbz8NCiAgYXBpX3Jlc3VsdGF0czogaHR0cHM6Ly9odWJlYXUuZWF1ZnJhbmNlLmZyL2FwaS92YmV0YS9oeWRyb2Jpby9pbmRpY2VzPw0KICBudW1fZGVwYXJ0ZW1lbnQ6ICcyMiwyOSwzNSw1Niw1MCw0NCw0OSw1MycNCiAgY29kZXNfcGFyYW1ldHJlczogJzcwMzYsNTkxMCwxMDIyLDU4NTYsMjkyOCw2OTU5LDY5NTEsNjk1NSwxMDAwLDI1MjcnDQogIGRhdGVfZGVidXQ6ICcyMDE3LTAxLTAxJw0KICBkYXRlX2ZpbjogJzIwMTktMTItMzEnDQogIHBhZ2luYXRpb246IDUwMDANCiAgcGF0aF9kYXRhdml6OiBPOlwwNC5EQVRBVklTVUFMSVNBVElPTlxJTkRJQ0FURVVSU19CSU9MT0dJRVxEQ0VfRVRBVF9CSU9MT0dJUVVFDQotLS0NCg0KYGBge3Igc2V0dXB9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQoNCiMgUGFja2FnZXMgdXRpbGlzw6lzDQpsaWJyYXJ5KGNvbmZpZykgIyBVdGlsaXNhdGlvbiBkJ3VuIGZpY2hpZXIgZGUgY29uZmlndXJhdGlvbiwgY2YgIGh0dHBzOi8vZGIucnN0dWRpby5jb20vYmVzdC1wcmFjdGljZXMvbWFuYWdpbmctY3JlZGVudGlhbHMvI3N0b3JlZC1pbi1hLWZpbGUtd2l0aC1jb25maWcNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShEQkkpICMgcG91ciBsZXMgY29ubmV4aW9ucyBhdXggQkREDQpsaWJyYXJ5KFJQb3N0Z3JlU1FMKSAjIGRyaXZlciBwb3N0Z3Jlcw0KbGlicmFyeShSTWFyaWFEQikgIyBkcml2ZXIgTWFyaWFEQg0KbGlicmFyeShSTXlTUUwpICMgZHJpdmVyIE15U1FMDQpsaWJyYXJ5KGx1YnJpZGF0ZSkgIyBjYWxjdWwgc3VyIGxlcyBkYXRlcw0KDQpsaWJyYXJ5KGh0dHIpICMNCmxpYnJhcnkoanNvbmxpdGUpICMgR2VzdGlvbiBmb3JtYXQganNvbg0KDQpsaWJyYXJ5KCJyZWFkeGwiKSAjIExlY3R1cmUgZGUgZmljaGllcnMgeGxzeA0KDQpsaWJyYXJ5KHNmKSAjIFRyYWl0ZW1lbnQgZGUgZG9ubsOpZXMgZ8Opb3LDqWbDqXJlbmPDqWVzDQpsaWJyYXJ5KG1hcHZpZXcpICMgQWZmaWNoYWdlIGRlIGRvbm7DqWVzIGfDqW9yw6lmw6lyZW5jw6llcw0KbGlicmFyeShwbG90bHkpICMgR3JhcGhpcXVlcyBpbnRlcmFjdGlmcw0KbGlicmFyeShsZWFmbGV0KSAjIENhcnRlcyBpbnRlcmFjdGl2ZXMNCg0KIyBGb25jdGlvbiBwb3VyIHJlcXXDqnRlciBsJ0FQSSBIdWInRWF1DQoNCmdldF9odWJlYXUgPC0gZnVuY3Rpb24ocGF0aCwgcXVlcnkpIHsNCiAgDQogIHJlc3BvbnNlIDwtIEdFVCh1cmwgPSBwYXRoLCBxdWVyeSA9IHF1ZXJ5KSAlPiUgDQogIGNvbnRlbnQoYXMgPSAidGV4dCIsIGVuY29kaW5nID0gIlVURi04IikgJT4lDQogIGZyb21KU09OKGZsYXR0ZW4gPSBUUlVFKQ0KICANCmRhdGEgPSByZXNwb25zZSRkYXRhICMgTGUgamV1IGRlIGRvbm7DqWVzIGVzdCBkYW5zIGwnb2JqZXQgImRhdGEiDQoNCmlmKHJlc3BvbnNlJGNvdW50ID4gcXVlcnkkc2l6ZSkgeyAjIFNpIGxhIHRhaWxsZSBkZSBwYWdlIGVzdCBwbHVzIHBldGl0ZSBxdWUgbGUgbmIgZGUgcsOpc3VsdGF0cywgZmFpcmUgdW5lIGJvdWNsZSBwb3VyIGxlcyBwYWdlcyBzdWl2YW50ZXMNCiAgDQpwYWdlcyA8LSBjZWlsaW5nKHJlc3BvbnNlJGNvdW50L3F1ZXJ5JHNpemUpDQoNCiMgQWZmaWNoYWdlIGQndW5lIGJhcnJlIGRlIHByb2dyZXNzaW9uDQpwYiA8LSB3aW5Qcm9ncmVzc0Jhcih0aXRsZSA9ICJSw6ljdXDDqXJhdGlvbiBkZXMgY2hyb25pcXVlcyIsIG1pbiA9IDAsDQogICAgICAgICAgICAgICAgICAgICBtYXggPSBwYWdlcywgd2lkdGggPSAzMDApDQoNCmZvcihpIGluIDI6cGFnZXMpew0KICANCiAgcXVlcnkkcGFnZSA8LSBpDQogIA0KICByZXNwb25zZV9pIDwtIEdFVCh1cmwgPSBwYXRoLCBxdWVyeSA9IHF1ZXJ5KSAlPiUgDQogIGNvbnRlbnQoYXMgPSAidGV4dCIsIGVuY29kaW5nID0gIlVURi04IikgJT4lDQogIGZyb21KU09OKGZsYXR0ZW4gPSBUUlVFKQ0KDQpkYXRhIDwtIHJiaW5kKGRhdGEsIHJlc3BvbnNlX2kkZGF0YSkgIyBDb25jYXTDqW5hdGlvbiBkZXMgbGlnbmVzIHLDqWN1cMOpcsOpZXMNCg0Kc2V0V2luUHJvZ3Jlc3NCYXIocGIsIGksIHRpdGxlPXBhc3RlKCByb3VuZChpL3BhZ2VzKjEwMCwgMCksICIlIGNoYXJnw6kiKSkgIyBBdmFuY2VtZW50IGRlIGxhIGJhcnJlIGRlIHByb2dyZXNzaW9uDQoNCn0gIyBGaW4gYm91Y2xlIGZvcg0KDQpjbG9zZShwYikNCg0KfSAjIEZpbiBpZg0KDQpyZXR1cm4oZGF0YSkNCiAgfSAjIEZpbiBnZXRfaHViZWF1DQoNCiMgSW5zZXJ0aW9uIGRlIGRvbm7DqWVzDQoNCmRiLmluc2VydGlvbiA8LSBmdW5jdGlvbihjb24sdGFibGUsZGF0YSkgew0KICANCiAgIyBEYXRlIGRlIGxhIGRlcm5pw6hyZSBkb25uw6llIGVuIGJhc2UNCiAgRGF0ZV9tYXggPC0gdGJsKGNvbiwgdGFibGUpICU+JSBzdW1tYXJpc2UoRGF0ZV9tYXggPSBtYXgoRGF0ZV9kZV9sYV9tZXN1cmUsIG5hLnJtID0gVFJVRSkpICU+JSBwdWxsKERhdGVfbWF4KQ0KICBpZihpcy5uYShEYXRlX21heCkpIERhdGVfbWF4IDwtIGFzLkRhdGUocGFyYW1zJGRhdGVfZGVidXQpLTENCiAgDQogICMgRG9ubsOpZXMgcGx1cyByw6ljZW50ZXMgw6AgaW5zw6lyZXINCiAgZGF0YSA8LSBkYXRhICU+JSBmaWx0ZXIoRGF0ZV9kZV9sYV9tZXN1cmUgPiBEYXRlX21heCkNCiAgDQogICMgRXhpc3RlLXQtaWwgZGVzIGRvbm7DqWVzIMOgIGluc8OpcmVyID8NCmlmIChpc1RSVUUoZGF0YSAlPiUgdGFsbHkoKSA+IDApKSB7DQogIA0KICAjIEluc2VydGlvbiBkZXMgbm91dmVsbGVzIGxpZ25lcw0KICBkYldyaXRlVGFibGUoY29uLCB0YWJsZSwgZGF0YSwgb3ZlcndyaXRlPUZBTFNFLCBhcHBlbmQ9VFJVRSwNCiAgICAgICAgICAgICBmaWxlRW5jb2Rpbmc9ImxhdGluMSIpDQogIA0KICAjIEhvcm9kYXRlIGVuIGNvbW1lbnRhaXJlDQptaXNlX2Ffam91ciA8LSBwYXN0ZTAoZm9ybWF0LkRhdGUoU3lzLkRhdGUoKSwiJWQvJW0vJVkiKSwgIiA6IEFjdHVhbGlzYXRpb24gIixwYXJhbXMkYWN0dWFsaXNhdGlvbikNCg0KZGJHZXRRdWVyeShjb24sIHBhc3RlMCgiQUxURVIgVEFCTEUgIix0YWJsZSwiIENPTU1FTlQgPSAnIixtaXNlX2Ffam91ciwiJzsiKSkNCg0KcGFzdGUwKCJEb25uw6llcyBpbnPDqXLDqWVzIDogIixkYXRhJT4ldGFsbHkpDQoNCn0NCiAgZWxzZSB7DQogICAgDQogICAgcGFzdGUwKCJBdWN1bmUgZG9ubsOpZSDDoCBpbnPDqXJlciBkZXB1aXMgbGUgIiwgZm9ybWF0LkRhdGUoRGF0ZV9tYXgsIiVkLyVtLyVZIikpDQogIA0KICAgIH0NCiAgDQp9ICMgZmluIGRiLmluc2VydGlvbg0KYGBgDQoNCiMgQ29ubmV4aW9uIGF1eCBiYXNlcyBkZSBkb25uw6llcyBwb3VyIGwnaW50w6lncmF0aW9uIGRlcyBkb25uw6llcyBpbXBvcnTDqWVzDQoNCmBgYHtyIGNvbm5leGlvbl9iZH0NCg0KIyBCYXNlIE1hcmlhZGIgT0VCIC0gZWF1X3RiaQ0KY29uX2VhdV90YmkgPC0gZGJDb25uZWN0KFJNYXJpYURCOjpNYXJpYURCKCksIGRlZmF1bHQuZmlsZSA9ICcuLi8uLi8ubXkuY25mJywgZ3JvdXBzPSJteXNxbF9vZWIiLA0KZGJuYW1lID0gImVhdV90YmkiKQ0KDQojIEJhc2UgTWFyaWFEQiBPRUIgLSByw6lmw6lyZW50aWVscw0KY29uX3JlZmVyZW50aWVscyA8LSBkYkNvbm5lY3QoUk1hcmlhREI6Ok1hcmlhREIoKSwgZGVmYXVsdC5maWxlID0gJy4uLy4uLy5teS5jbmYnLCBncm91cHM9Im15c3FsX29lYiIsDQpkYm5hbWUgPSAiZWF1X3JlZmVyZW50aWVscyIpDQoNCiMgQmFzZSBQb3N0Z3JlcyAoZMOpdmVsb3BwZW1lbnQpDQoNCmNvbmYgPC0gY29uZmlnOjpnZXQoInBvc3RncmVzX2RldiIpDQoNCmNvbl9wb3N0Z3Jlc3FsX2RldiA8LSBEQkk6OmRiQ29ubmVjdChvZGJjOjpvZGJjKCksDQogICAgICAgICAgICAgICAgICAgICAgICAgIERyaXZlciAgICAgICA9IGNvbmYkZHJpdmVyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBzZXJ2ZXJuYW1lICAgPSBjb25mJHNlcnZlciwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgVUlEID0gY29uZiR1aWQsDQogICAgICAgICAgICAgICAgICAgICAgICAgIFBXRCA9IGNvbmYkcHdkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBQb3J0ID0gY29uZiRwb3J0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhYmFzZSA9ICdlYXUnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmNvZGluZyA9ICJsYXRpbjEiKQ0KYGBgDQojIENvbGxlY3RlIGRlcyBkb25uw6llcyBkZSBsYSBiYXNlIE5hw69hZGVzIMOgIHBhcnRpciBkZSBsJ0FQSSBIeWRyb2Jpb2xvZ2llIGR1IHBvcnRhaWwgSHViJ2VhdQ0KDQoqKkRvY3VtZW50YXRpb24gZGUgbCdBUEkgOioqIGh0dHBzOi8vaHViZWF1LmVhdWZyYW5jZS5mci9wYWdlL2FwaS1oeWRyb2Jpb2xvZ2llDQoNCmBgYHtyIGh1YmVhdX0NCg0KIyBSw6ljdXDDqXJhdGlvbiBkZXMgaW5mb3Mgc3VyIGxlcyBzdGF0aW9ucyBkZSBtZXN1cmUNCg0KIyBMaXN0ZSBkZXMgcGFyYW3DqHRyZXMgZGUgbGEgcmVxdcOqdGUNCnF1ZXJ5X3NpdGVzID0gbGlzdCgNCiAgY29kZV9kZXBhcnRlbWVudD0gcGFyYW1zJG51bV9kZXBhcnRlbWVudCwNCiAgZm9ybWF0PSdqc29uJywNCiAgc2l6ZT1wYXJhbXMkcGFnaW5hdGlvbg0KICApDQoNCiMgUmVxdcOqdGUgZGVzIHN0YXRpb25zDQpodWJlYXVfc2l0ZXMgPC0gZ2V0X2h1YmVhdShwYXRoID0gcGFyYW1zJGFwaV9zaXRlcywgcXVlcnkgPSBxdWVyeV9zaXRlcykNCg0KIyBDb252ZXJzaW9uIGF1IGZvcm1hdCBnw6lvZ3JhcGhpcXVlDQpzaXRlcyA8LSBzdF9hc19zZihodWJlYXVfc2l0ZXMsIGNvb3JkcyA9IGMoImNvb3Jkb25uZWVfeCIsICJjb29yZG9ubmVlX3kiKSwgDQogICAgY3JzID0gMjE1NCwgYWdyID0gImNvbnN0YW50IikNCg0KIyBSw6ljdXDDqXJhdGlvbiBkZXMgcsOpc3VsdGF0cyBkJ2luZGljZXMgYmlvbG9naXF1ZXMNCg0KIyBMaXN0ZSBkZXMgcGFyYW3DqHRyZXMgZGUgbGEgcmVxdcOqdGUgKGxvdCAxIGRlIGTDqXBhcnRlbWVudHMpDQpxdWVyeV9yZXN1bHRhdHMgPSBsaXN0KA0KICAgICAgICAgICAgICAgICAjY29kZV9kZXBhcnRlbWVudCA9IHBhcmFtcyRudW1fZGVwYXJ0ZW1lbnQsDQogICAgICAgICAgICAgICAgIGNvZGVfZGVwYXJ0ZW1lbnQgPSAiMjksMjIsMzUiLA0KICAgICAgICAgICAgICAgICBjb2RlX2luZGljZSA9IHBhcmFtcyRjb2Rlc19wYXJhbWV0cmVzLA0KICAgICAgICAgICAgICAgICAjbGVzIHBhcmFtw6h0cmVzIGRhdGVfZGVidXQgZXQgZGF0ZV9maW4gY3LDqWVudCB1biBidWcgZGFucyBsJ0FQSSBodHRwczovL2dpdGh1Yi5jb20vQlJHTS9odWJlYXUvaXNzdWVzLzgxDQogICAgICAgICAgICAgICAgICNkYXRlX2RlYnV0X3ByZWxldmVtZW50ID0gcGFyYW1zJGRhdGVfZGVidXQsDQogICAgICAgICAgICAgICAgICNkYXRlX2Zpbl9wcmVsZXZlbWVudCA9IHBhcmFtcyRkYXRlX2ZpbiwNCiAgICAgICAgICAgICAgICAgc2l6ZSA9IHBhcmFtcyRwYWdpbmF0aW9uLA0KICAgICAgICAgICAgICAgICBmb3JtYXQgPSAnanNvbicpDQoNCiMgUmVxdcOqdGUgZGVzIGNocm9uaXF1ZXMNCmh1YmVhdV9yZXN1bHRhdHMgPC0gZ2V0X2h1YmVhdShwYXRoID0gcGFyYW1zJGFwaV9yZXN1bHRhdHMsIHF1ZXJ5PXF1ZXJ5X3Jlc3VsdGF0cykNCg0KIyBMaXN0ZSBkZXMgcGFyYW3DqHRyZXMgZGUgbGEgcmVxdcOqdGUgKGxvdCAyIGRlIGTDqXBhcnRlbWVudHMpDQpxdWVyeV9yZXN1bHRhdHMgPSBsaXN0KA0KICAgICAgICAgICAgICAgICAjY29kZV9kZXBhcnRlbWVudCA9IHBhcmFtcyRudW1fZGVwYXJ0ZW1lbnQsDQogICAgICAgICAgICAgICAgIGNvZGVfZGVwYXJ0ZW1lbnQgPSAiNTYsNTAsNDQsNDksNTMiLA0KICAgICAgICAgICAgICAgICBjb2RlX2luZGljZSA9IHBhcmFtcyRjb2Rlc19wYXJhbWV0cmVzLA0KICAgICAgICAgICAgICAgICAjbGVzIHBhcmFtw6h0cmVzIGRhdGVfZGVidXQgZXQgZGF0ZV9maW4gY3LDqWVudCB1biBidWcgZGFucyBsJ0FQSSBodHRwczovL2dpdGh1Yi5jb20vQlJHTS9odWJlYXUvaXNzdWVzLzgxDQogICAgICAgICAgICAgICAgICNkYXRlX2RlYnV0X3ByZWxldmVtZW50ID0gcGFyYW1zJGRhdGVfZGVidXQsDQogICAgICAgICAgICAgICAgICNkYXRlX2Zpbl9wcmVsZXZlbWVudCA9IHBhcmFtcyRkYXRlX2ZpbiwNCiAgICAgICAgICAgICAgICAgc2l6ZSA9IHBhcmFtcyRwYWdpbmF0aW9uLA0KICAgICAgICAgICAgICAgICBmb3JtYXQgPSAnanNvbicpDQoNCiMgUmVxdcOqdGUgZGVzIGNocm9uaXF1ZXMNCmh1YmVhdV9yZXN1bHRhdHMgPC0gZ2V0X2h1YmVhdShwYXRoID0gcGFyYW1zJGFwaV9yZXN1bHRhdHMsIHF1ZXJ5PXF1ZXJ5X3Jlc3VsdGF0cyklPiV1bmlvbihodWJlYXVfcmVzdWx0YXRzKQ0KDQojIENvbnZlcnNpb24gYXUgZm9ybWF0IGfDqW9ncmFwaGlxdWUNCnJlc3VsdGF0cyA8LSBzdF9hc19zZihodWJlYXVfcmVzdWx0YXRzLCBjb29yZHMgPSBjKCJjb29yZG9ubmVlX3giLCAiY29vcmRvbm5lZV95IiksIA0KICAgIGNycyA9IDIxNTQsIGFnciA9ICJjb25zdGFudCIpDQpgYGANCkxlcyBzdGF0aW9ucyBkZSBtZXN1cmUgZXQgbGVzIGNocm9uaXF1ZXMgZCdhbmFseXNlcyBzb250IHLDqWN1cMOpcsOpZXMgc3VyIGxlcyBkw6lwYXJ0ZW1lbnRzIGByIHBhcmFtcyRudW1fZGVwYXJ0ZW1lbnRgIHN1ciBsYSBww6lyaW9kZSAsIHZpYSBsYSBmb25jdGlvbiAqZ2V0X2h1YmVhdShwYXRoLCBxdWVyeSkqIGTDqWZpbmllIGNpLWRlc3N1cy4NCg0KLSBwYXRoIDogYWRyZXNzZSBkZSBsJ0FQSQ0KLSBxdWVyeSA6IGxpc3RlIGRlcyBwYXJhbcOodHJlcyBkZSByZXF1w6p0ZSBhdmVjIGxldXJzIHZhbGV1cnMgKGNvZGVfKQ0KDQpMZXMgZMOpcGFydGVtZW50cyBzw6lwYXLDqXMgZW4gZGV1eCBsb3RzIHBvdXIgcmVzcGVjdGVyIGxlcyBjb250cmFpbnRlcyBkZSBuYiBkZSBsaWduZXMgdG90YWxlcyByw6ljdXDDqXLDqWVzIHBhciByZXF1w6p0ZS4NCg0KIyBJbXBvcnQgZGVzIHLDqWbDqXJlbnRpZWxzIGfDqW9ncmFwaGlxdWVzDQoNCkxlcyBwb2x5Z29uZXMgZGVzIHRlcnJpdG9pcmVzIGRlIFNBR0UgYnJldG9ucyBzb250IHLDqWN1cMOpcsOpZXMgZGVwdWlzIGxlIGZsdXggV0ZTIGRlIGxhIERSRUFMIEJyZXRhZ25lLCBpbHMgY29ycmVzcG9uZGVudCBhdSBww6lyaW3DqHRyZSBnw6lvZ3JhcGhpcXVlIGRlcyBkb25uw6llcyDDoCBiYW5jYXJpc2VyLg0KDQpgYGB7ciBjb3VjaGVfc2FnZXN9DQojIGNvdWNoZSBkZXMgU0FHRXMgYnJldG9ucyBkZXB1aXMgR2VvYnJldGFnbmUNCnNhZ2VzIDwtIHN0X3JlYWQoImh0dHBzOi8vZ2VvYnJldGFnbmUuZnIvZ2Vvc2VydmVyL2RyZWFsX2Ivc2FnZV9kcmVhbC93ZnM/U0VSVklDRT1XRlMmUkVRVUVTVD1HZXRDYXBhYmlsaXRpZXMiKQ0KDQojIHBiIHBhcyBtb3llbiBkZSBtYW5pcHVsZXIgY2V0IG9iamV0ID0+IHNvbHV0aW9uIHN1cg0KIyBodHRwczovL2dpcy5zdGFja2V4Y2hhbmdlLmNvbS9xdWVzdGlvbnMvMzg5ODE0L3Itc3QtY2VudHJvaWQtZ2Vvcy1lcnJvci11bmtub3duLXdrYi10eXBlLTEyLzM4OTg1NCMzODk4NTQNCmVuc3VyZV9tdWx0aXBvbHlnb25zIDwtIGZ1bmN0aW9uKFgpIHsNCiAgdG1wMSA8LSB0ZW1wZmlsZShmaWxlZXh0ID0gIi5ncGtnIikNCiAgdG1wMiA8LSB0ZW1wZmlsZShmaWxlZXh0ID0gIi5ncGtnIikNCiAgc3Rfd3JpdGUoWCwgdG1wMSkNCiAgZ2RhbFV0aWxpdGllczo6b2dyMm9ncih0bXAxLCB0bXAyLCBmID0gIkdQS0ciLCBubHQgPSAiTVVMVElQT0xZR09OIikNCiAgWSA8LSBzdF9yZWFkKHRtcDIpDQogIHN0X3NmKHN0X2Ryb3BfZ2VvbWV0cnkoWCksIGdlb20gPSBzdF9nZW9tZXRyeShZKSkNCn0NCg0Kc2FnZXMgPC0gZW5zdXJlX211bHRpcG9seWdvbnMoc2FnZXMpDQoNCiMgY291Y2hlIGRlcyBIeWRyb8OpY29yw6lnaW9ucyBkZSBuaXZlYXUgMg0KDQpoZXIyIDwtIHN0X3JlYWQoImh0dHBzOi8vc2VydmljZXMuc2FuZHJlLmVhdWZyYW5jZS5mci9nZW8vbWRvP1NFUlZJQ0U9V0ZTJlZFUlNJT049Mi4wLjAmUkVRVUVTVD1HZXRGZWF0dXJlJnR5cGVuYW1lPUh5ZHJvZWNvcmVnaW9uMiIpJT4lDQogIHN0X3RyYW5zZm9ybSgyMTU0KQ0KYGBgDQojIFPDqWxlY3Rpb24gZGVzIHNpdGVzIGV0IGRlcyByw6lzdWx0YXRzIHN1ciBsZXMgdGVycml0b2lyZXMgZGVzIFNBR0UgYnJldG9ucw0KDQpMZXMgc2l0ZXMgZXQgcsOpc3VsdGF0cyB0w6lsw6ljaGFyZ8OpcyBzb250IHPDqWxlY3Rpb25uw6lzIHN1ciBsZSBww6lyaW3DqHRyZSBnw6lvZ3JhcGhpcXVlIHJldGVudSwgcGFyIGpvaW50dXJlIGF2ZWMgbGEgY291Y2hlIGRlcyBTQUdFLg0KDQpgYGB7ciBzZWxlY3Rpb24gc3VyIGxlcyB0ZXJyaXRvaXJlcyBkZXMgU0FHRXN9DQoNCnNpdGVzX3NhZ2VzIDwtIHNpdGVzICU+JSANCiAgc3Rfam9pbihzYWdlcykgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKGNkX3NhZ2UpKQ0KDQpyZXN1bHRhdHNfc2FnZXMgPC0gcmVzdWx0YXRzICU+JSANCiAgc3Rfam9pbihzYWdlcykgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKGNkX3NhZ2UpKSAlPiUgDQogIHN0X2pvaW4oaGVyMikNCg0KYGBgDQoNCiMjIExlcyBzaXRlcyBmb250LWlscyBwYXJ0aWUgZHUgcsOpc2VhdSBSQ1MgPw0KDQpMJ2luZm9ybWF0aW9uIHN1ciBsZSByw6lzZWF1IGRlIHN1aXZpIGVzdCBpbmNsdXNlIGRhbnMgbGVzIGRvbm7DqWVzIHN1ciBsZXMgc2l0ZXMgZm91cm5pZXMgcGFyIGwnQVBJLiBMZSByw6lzZWF1IFJDUyBjb3JyZXNwb25kIGF1IGNvZGUgU0FORFJFIDogMDAwMDAwMDA1Mg0KDQpgYGB7ciBzaXRlc19yY3N9DQpzaXRlc19yY3MgPC0gc2l0ZXNfc2FnZXMgJT4lDQogICMgcm93d2lzZSBwb3VyIHByw6ljaXNlciBxdWUgbGVzIG9ww6lyYXRpb25zIHNlIGZvbnQgcG91ciBjaGFxdWUgbGlnbmUNCiAgcm93d2lzZSgpJT4lDQogICMgTGUgY29kZSAwMDAwMDAwMDUyIChSQ1MpIGVzdCBkYW5zIGxhIGxpc3RlIGRlcyBjb2Rlc19yw6lzZWF1eA0KICBtdXRhdGUoaW5jbHVzX3JjcyA9ICcwMDAwMDAwMDUyJyAlaW4lIHVubGlzdChjb2Rlc19yZXNlYXV4KSklPiUNCiAgc2VsZWN0KGNvZGVfc3RhdGlvbl9oeWRyb2JpbywgaW5jbHVzX3JjcykNCmBgYA0KDQojIyBDYXJ0ZSBkZXMgc2l0ZXMgaW1wb3J0w6lzIHZpYSBIdWInZWF1LCBwYXIgZMOpcGFydGVtZW50DQoNCmBgYHtyIGNhcnRlX3NpdGVzfQ0Kc2l0ZXNfc2FnZXMgJT4lDQogIGdncGxvdCgpKw0KICBnZW9tX3NmKGRhdGE9c2FnZXMpKw0KZ2VvbV9zZihhZXMoY29sb3I9bGliZWxsZV9kZXBhcnRlbWVudCkpDQpgYGANCiMjIENhcnRlIGRlcyByw6lzdWx0YXRzIGltcG9ydMOpcyB2aWEgSHViJ2VhdSwgcGFyIHF1YWxpZmljYXRpb24NCg0KYGBge3IgY2FydGVfcmVzdWx0YXRzfQ0KcmVzdWx0YXRzX3NhZ2VzICU+JQ0KICBnZ3Bsb3QoKSsNCiAgZ2VvbV9zZihkYXRhPXNhZ2VzKSsNCmdlb21fc2YoYWVzKGNvbG9yPWxpYmVsbGVfcXVhbGlmaWNhdGlvbikpDQpgYGANCiMjIEV4ZW1wbGUgZGUgcsOpc3VsYXQgOiBDYXJ0ZSBkZSBsJ2luZGljZSBEaWF0b23DqWVzIChJQkQpDQpgYGB7ciBjYXJ0ZV9pbmRpY2V9DQpyZXN1bHRhdHNfc2FnZXMgJT4lDQogIGZpbHRlcihjb2RlX2luZGljZSA9PSAnNTg1NicpJT4lICM1ODU2IElCRCBJbmRpY2UgRGlhdG9tw6llcw0KICBnZ3Bsb3QoKSsNCiAgZ2VvbV9zZihkYXRhPXNhZ2VzKSsNCmdlb21fc2YoYWVzKGNvbG9yPXJlc3VsdGF0X2luZGljZSkpDQpgYGANCg0KIyBKb2ludHVyZSBhdmVjIGxlcyB0YWJsZXMgcsOpZsOpcmVudGllbHMNCg0KIyMgU2l0ZXMgaW5jb25udXMNCg0KTGVzIHNpdGVzIGltcG9ydMOpcyBzb250IGNvbXBhcsOpcyDDoCBsYSB0YWJsZSBkZXMgc2l0ZXMgYmFuY2FyaXPDqXMgcHLDqWPDqWRlbW1lbnQsIHBhciBjb2RlIFNBTkRSRS4NCg0KTGlzdGUgZGVzIHNpdGVzIGluY29ubnVzIDoNCg0KYGBge3Igc2l0ZXNfaW5jb25udXN9DQpzaXRlc19pbmNvbm51cyA8LSBzaXRlc19zYWdlcyAlPiUNCiAgIyB0YWJsZXMgZGVzIHNpdGVzDQogIGxlZnRfam9pbih0YmwoY29uX3Bvc3RncmVzcWxfZGV2LCBkYnBseXI6OmluX3NjaGVtYSgiZWF1X3N0cnVjdHVyZSIsInNpdGUiKSksIGJ5PWMoImNvZGVfc3RhdGlvbl9oeWRyb2JpbyIgPSAiY29kZSIpLCBjb3B5PVRSVUUsIHN1ZmZpeCA9IGMoIiIsICIuc2l0ZSIpKSU+JQ0KICBmaWx0ZXIoaXMubmEoc2l0ZV9pZCkpDQoNCnNpdGVzX2luY29ubnVzICU+JSANCiAgZGlzdGluY3QoY29kZV9zdGF0aW9uX2h5ZHJvYmlvLCBsaWJlbGxlX3N0YXRpb25faHlkcm9iaW8pDQpgYGANCkNhcnRlIGRlcyBzaXRlcyBpbmNvbm51cyA6DQoNCmBgYHtyIGNhcnRlX3NpdGVzX2luY29ubnVzfQ0Kc2l0ZXNfaW5jb25udXMgJT4lDQogIGdncGxvdCgpKw0KICBnZW9tX3NmKGRhdGE9c2FnZXMpKw0KZ2VvbV9zZihhZXMoY29sb3I9bGliZWxsZV9kZXBhcnRlbWVudCkpDQpgYGANCg0KIyMjIE1pc2UgZW4gZm9ybWUgZGVzIG5vdXZlYXV4IHNpdGVzIGVuIGZvbmN0aW9uIGR1IHNjaGVtYSBkZSBsYSB0YWJsZSBkZXMgc2l0ZXMgYmFuY2FyaXPDqXMNCg0KYGBge3IgdGFibGUgc2l0ZXNfaW5jb25udXN9DQoNCmluc2VydF9zaXRlcyA8LSBzaXRlc19pbmNvbm51cyAlPiUNCiAgbXV0YXRlKGNvb3JkX3ggPSBzdF9jb29yZGluYXRlcyhzdF90cmFuc2Zvcm0oZ2VvbWV0cnksIDIxNTQpKVssMV0sDQogICAgICAgICBjb29yZF95ID0gc3RfY29vcmRpbmF0ZXMoc3RfdHJhbnNmb3JtKGdlb21ldHJ5LCAyMTU0KSlbLDJdLA0KICAgICAgICAgbG9uZ2l0dWRlX3dnczg0ID0gc3RfY29vcmRpbmF0ZXMoc3RfdHJhbnNmb3JtKGdlb21ldHJ5LCA0MzI2KSlbLDFdLA0KICAgICAgICAgbGF0aXR1ZGVfd2dzODQgPSBzdF9jb29yZGluYXRlcyhzdF90cmFuc2Zvcm0oZ2VvbWV0cnksIDQzMjYpKVssMl0sDQogICAgICAgICB0eXBlc2l0ZV9pZCA9ICcxJywNCiAgICAgICAgIHByb2plY3Rpb25faWQgPSAnMjE1NCcsDQogICAgICAgICBzb3VyY2UgPSAnT0ZCL05BSUFERVMnLA0KICAgICAgICAgbWFqID0gZm9ybWF0KFN5cy5EYXRlKCksIiVZLSVtLSVkIikNCiAgICAgICAgICklPiUgDQogIGFzX3RpYmJsZSgpICU+JQ0Kc2VsZWN0KGNvZGUgPSBjb2RlX3N0YXRpb25faHlkcm9iaW8sDQpsaWJlbGxlID0gbGliZWxsZV9zdGF0aW9uX2h5ZHJvYmlvLA0KdHlwZXNpdGVfaWQsDQpjb29yZF94LA0KY29vcmRfeSwNCnByb2plY3Rpb25faWQsDQpsb25naXR1ZGVfd2dzODQsDQpsYXRpdHVkZV93Z3M4NCwNCnNvdXJjZSwNCm1haikNCg0KaW5zZXJ0X3NpdGVzDQoNCmBgYA0KIyMjIEluc2VydGlvbiBkZXMgbm91dmVhdXggc2l0ZXMgDQoNCmBgYHtyIGluc2VydCBzaXRlc19pbmNvbm51cywgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCg0Kc2Y6OmRiV3JpdGVUYWJsZShjb25uID0gY29uX3Bvc3RncmVzcWxfZGV2LCBuYW1lID0gSWQoc2NoZW1hID0gImVhdV9zdHJ1Y3R1cmUiLHRhYmxlID0gInNpdGUiKSwgdmFsdWUgPSBpbnNlcnRfc2l0ZXMsIG92ZXJ3cml0ZT1GQUxTRSwgYXBwZW5kPVRSVUUsIGZpbGVFbmNvZGluZz0ibGF0aW4xIikNCmBgYA0KDQojIyBUYWJsZSBkZSBjb3JyZXNwb25kYW5jZSBzaXRlIC8gdGVycml0b2lyZXMgKEVHQSAtIEVudGl0w6lzIEdlb2dyYXBoaXF1ZXMgZXQgQWRtaW5pc3RyYXRpdmVzKQ0KDQpMYSB0YWJsZSBkZSBjb3JyZXNwb25kYW5jZSBlc3QgaXNzdWUgZGUgbGEgam9pbnR1cmUgc3BhdGlhbGUgZW50cmUgbGEgdGFibGUgZGVzIHNpdGVzIGV0IGxlcyBkaWZmw6lyZW50ZXMgw6ljaGVsbGVzIGRlIHRlcnJpdG9pcmVzIGJyZXRvbnMgOiBSw6lnaW9uLCBEw6lwYXJ0ZW1lbnRzLCBFUENJLCBTQUdFLCBDb250cmF0cyBkZSB0ZXJyaXRvaXJlcw0KDQpgYGB7ciBpbXBvcnRfZWF1X2NvcnJlc3BvbmRhbmNlX3NpdGVfZWdhfQ0KY29ycmVzcG9uZGFuY2Vfc2l0ZV9lZ2EgPC0gdGJsKGNvbl9wb3N0Z3Jlc3FsX2RldiwgZGJwbHlyOjppbl9zY2hlbWEoImVhdV9yZWZlcmVudGllbCIsImdlb19jb3JyZXNwb25kYW5jZV9zaXRlX2VnYSIpKSU+JQ0KICBtdXRhdGUodHlwZXNpdGUgPSAnU0lURScpDQoNCmNvcnJlc3BvbmRhbmNlX3NpdGVfZWdhDQpgYGANCiMjIExpc3RlIGRlcyBwYXJhbcOodHJlcyBiYW5jYXJpc8Opcw0KDQpMYSBsaXN0ZSBkZXMgcGFyYW3DqHRyZXMgZXN0IGlzc3VlIGR1IGRpY3Rpb25uYWlyZSBuYXRpb25hbCBkZXMgZG9ubsOpZXMgc3VyIGwnZWF1IChTQU5EUkUpLg0KDQpgYGB7ciBpbXBvcnQgcGFyYW1ldHJlc30NCg0KbGlzdGVfcGFyYW1ldHJlcyA8LSBhcy5saXN0KHN0cnNwbGl0KHBhcmFtcyRjb2Rlc19wYXJhbWV0cmVzLCAiLCIpW1sxXV0pDQoNCnBhcmFtZXRyZXMgPC0gdGJsKGNvbl9wb3N0Z3Jlc3FsX2RldiwgZGJwbHlyOjppbl9zY2hlbWEoImVhdV9zdHJ1Y3R1cmUiLCJwYXJhbWV0cmUiKSklPiUNCiAgY29sbGVjdCgpICU+JQ0KICBmaWx0ZXIoY29kZSAlaW4lIGxpc3RlX3BhcmFtZXRyZXMpDQoNCnBhcmFtZXRyZXMNCmBgYA0KDQojIyBTZXVpbHMgZGUgcXVhbGl0w6kgRENFDQoNCkxhIGxpc3RlIGRlcyBzZXVpbHMgZGUgcXVhbGl0w6kgZXN0IHByb2R1aXRlIMOgIHBhcnRpciBkZSBsJ1tBcnLDqnTDqSBkdSAyNyBqdWlsbGV0IDIwMThdKGh0dHBzOi8vd3d3LmxlZ2lmcmFuY2UuZ291di5mci9qb3JmL2FydGljbGVfam8vSk9SRkFSVEkwMDAwMzczNDc3ODIpIGRpc3BvbmlibGUgc3VyIExlZ2lmcmFuY2VbXmZvb3Rub3RlXSANCg0KW15mb290bm90ZV0gW0FycsOqdMOpIGR1IDI3IGp1aWxsZXQgMjAxOCBtb2RpZmlhbnQgbCdhcnLDqnTDqSBkdSAyNSBqYW52aWVyIDIwMTAgcmVsYXRpZiBhdXggbcOpdGhvZGVzIGV0IGNyaXTDqHJlcyBkJ8OpdmFsdWF0aW9uIGRlIGwnw6l0YXQgw6ljb2xvZ2lxdWUsIGRlIGwnw6l0YXQgY2hpbWlxdWUgZXQgZHUgcG90ZW50aWVsIMOpY29sb2dpcXVlIGRlcyBlYXV4IGRlIHN1cmZhY2UgcHJpcyBlbiBhcHBsaWNhdGlvbiBkZXMgYXJ0aWNsZXMgUi4gMjEyLTEwLCBSLiAyMTItMTEgZXQgUi4gMjEyLTE4IGR1IGNvZGUgZGUgbCdlbnZpcm9ubmVtZW50IC0gTMOpZ2lmcmFuY2UgKGxlZ2lmcmFuY2UuZ291di5mcildKGh0dHBzOi8vd3d3LmxlZ2lmcmFuY2UuZ291di5mci9qb3JmL2FydGljbGVfam8vSk9SRkFSVEkwMDAwMzczNDc3ODIpDQoNCmBgYHtyIGltcG9ydCBzZXVpbHMgZGUgcXVhbGl0w6l9DQoNCmNsYXNzZXNfcXVhbGl0ZSA8LSB0YmwoY29uX3Bvc3RncmVzcWxfZGV2LCBkYnBseXI6OmluX3NjaGVtYSgiZWF1X3N0cnVjdHVyZSIsImpvaW5fcGFyYW1ldHJlX2NsYXNzZSIpKSU+JQ0KICBsZWZ0X2pvaW4odGJsKGNvbl9wb3N0Z3Jlc3FsX2RldiwgZGJwbHlyOjppbl9zY2hlbWEoImVhdV9zdHJ1Y3R1cmUiLCJjbGFzc2UiKSksIGJ5PSJjbGFzc2VfaWQiKSU+JQ0KICBsZWZ0X2pvaW4oIHNlbGVjdCh0YmwoY29uX3Bvc3RncmVzcWxfZGV2LCBkYnBseXI6OmluX3NjaGVtYSgiZWF1X3N0cnVjdHVyZSIsInBhcmFtZXRyZSIpKSxwYXJhbWV0cmVfaWQsbGliZWxsZSwgY29kZSksIGJ5PSJwYXJhbWV0cmVfaWQiKSU+JQ0KICBjb2xsZWN0KCklPiUNCiAgZmlsdGVyKHBhcmFtZXRyZV9pZCAlaW4lIHBhcmFtZXRyZXMkcGFyYW1ldHJlX2lkLA0KICAgICAgICAgdmFsaWRlID09IDEpDQoNCmNsYXNzZXNfcXVhbGl0ZSAlPiUNCiAgc2VsZWN0KGxpYmVsbGUueSwgY29kZS55LCBwYXJhbWV0cmVfaWQsIGxpYmVsbGVfY291cnQsIGNvZGUueCwgYm9ybmVfaW5mX2luY2x1ZSwgYm9ybmVfc3VwX2V4Y2x1ZSwgc291cmNlKQ0KYGBgDQoNCiMjIFRhYmxlIGNvbXBsw6h0ZQ0KDQpUYWJsZSBkZXMgcsOpc3VsdGF0cyBhdmVjIGxlcyBhdHRyaWJ1dHMgZGVzIHLDqWbDqXJlbnRpZWxzDQoNCmBgYHtyIHRhYmxlIGluZGljZXN9DQp0YWJsZV9pbmRpY2VzIDwtIHJlc3VsdGF0c19zYWdlcyAlPiUNCiAgbXV0YXRlKENvb3JkWF9XR1M4NCA9IHN0X2Nvb3JkaW5hdGVzKHN0X3RyYW5zZm9ybShnZW9tZXRyeSwgNDMyNikpWywxXSwNCiAgICAgICAgIENvb3JkWV9XR1M4NCA9IHN0X2Nvb3JkaW5hdGVzKHN0X3RyYW5zZm9ybShnZW9tZXRyeSwgNDMyNikpWywyXSwNCiAgICAgICAgIGRhdGVfcHJlbGV2ZW1lbnQgPSBhcy5EYXRlKGRhdGVfcHJlbGV2ZW1lbnQpDQogICAgICAgICApJT4lDQogIGFzX3RpYmJsZSgpJT4lDQogICMgdGFibGVzIGRlcyBwYXJhbcOodHJlcw0KICBsZWZ0X2pvaW4ocGFyYW1ldHJlcywgYnk9YygiY29kZV9pbmRpY2UiPSJjb2RlIiksIHN1ZmZpeCA9IGMoIiIsICIucGFyYW1ldHJlIikpJT4lDQogICMgVW5pdMOpIGluY29ubnVlIC0tPiBjb2RlIHNhbmRyZSAwDQogICMgbiAobm9tYnJlKSAtLT4gY29kZSBzYW5kcmUgMjE0DQogICMg4oCwIHZzIFNNT1cgLS0+IGNvZGUgc2FuZHJlIDMyDQogIG11dGF0ZSh1bml0ZV9jb2RlID0gY2FzZV93aGVuKHVuaXRlX2luZGljZSA9PSAiVW5pdMOpIGluY29ubnVlIiB+ICcwJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bml0ZV9pbmRpY2UgPT0gIm4iIH4gJzIxNCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW5pdGVfaW5kaWNlID09ICLigLAgdnMgU01PVyIgfiAnMzInLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiB1bml0ZV9pbmRpY2UpLA0KICAgICAgICAgcmVzdWx0YXRfaW5kaWNlID0gaWZlbHNlKHJlc3VsdGF0X2luZGljZSA9PSA5OTksIE5BLCByZXN1bHRhdF9pbmRpY2UpKSAlPiUNCiAgIyB0YWJsZXMgZGVzIHVuaXTDqXMNCiAgbGVmdF9qb2luKHRibChjb25fcG9zdGdyZXNxbF9kZXYsIGRicGx5cjo6aW5fc2NoZW1hKCJlYXVfcmVmZXJlbnRpZWwiLCJ1bml0ZSIpKSwgYnk9YygidW5pdGVfY29kZSIgPSAiY29kZSIpLCBjb3B5PVRSVUUsIHN1ZmZpeCA9IGMoIiIsICIudW5pdGUiKSklPiUNCiAgIyB0YWJsZXMgZGVzIHNpdGVzDQogIGxlZnRfam9pbih0YmwoY29uX3Bvc3RncmVzcWxfZGV2LCBkYnBseXI6OmluX3NjaGVtYSgiZWF1X3N0cnVjdHVyZSIsInNpdGUiKSksIGJ5PWMoImNvZGVfc3RhdGlvbl9oeWRyb2JpbyIgPSAiY29kZSIpLCBjb3B5PVRSVUUsIHN1ZmZpeCA9IGMoIiIsICIuc2l0ZSIpKQ0KDQp0YWJsZV9pbmRpY2VzIA0KYGBgDQojIyMgU3ludGjDqHNlIGRlcyBkb25uw6llcyBtYW5xdWFudGVzDQoNCmBgYHtyIGRvbm5lZXNfbWFucXVhbnRlc30NCiMgU3ludGjDqHNlIGRlcyBkb25uw6llcyBtYW5xdWFudGVzDQp0YWJsZV9pbmRpY2VzICU+JQ0KICBzdW1tYXJpc2UoDQogICAgbmJfbGlnbmVzID0gbigpLA0KICAgIGxpZ25lc19zYW5zX3BhcmFtZXRyZSA9IHN1bShpcy5uYShwYXJhbWV0cmVfaWQpKSwNCiAgICBsaWduZXNfc2Fuc191bml0ZSA9IHN1bShpcy5uYSh1bml0ZV9pZCkpLA0KICAgIGxpZ25lc19zYW5zX3NpdGVzID0gc3VtKGlzLm5hKHNpdGVfaWQpKQ0KICApDQoNCiMgVW5pdMOpcyBpbmNvbm51ZXMgZGUgbGEgdGFibGUgZGUgcsOpZsOpcmVuY2UNCnRhYmxlX2luZGljZXMgJT4lIGZpbHRlcihpcy5uYSh1bml0ZV9pZCkpICU+JSBkaXN0aW5jdCh1bml0ZV9pbmRpY2UpDQoNCiMgU3RhdGlvbnMgaW5jb25udWVzIGRlIGxhIHRhYmxlIGRlcyBzaXRlcw0KdGFibGVfaW5kaWNlcyAlPiUgZmlsdGVyKGlzLm5hKHNpdGVfaWQpKSAlPiUgZGlzdGluY3QobGliZWxsZV9zdGF0aW9uX2h5ZHJvYmlvKQ0KDQojIExpc3RlIGRlcyBjb2RlcyBpbmRpY2VzDQp0YWJsZV9pbmRpY2VzICU+JSBkaXN0aW5jdChjb2RlX2luZGljZSkNCg0KYGBgDQoNCiMjIENsYXNzZXMgZGVzIGluZGljZXMNCg0KQXR0cmlidXRpb24gZGVzIGNsYXNzZXMgZGUgcXVhbGl0w6kgYXV4IHLDqXN1bHRhdHMgcGFyIHBhcmFtw6h0cmUgKGluZGljZSBiaW9sb2dpcXVlKQ0KDQpgYGB7ciBjbGFzc2VfaW5kaWNlc30NCnRhYmxlX2luZGljZXNfY2xhc3NlcyA8LSB0YWJsZV9pbmRpY2VzICU+JSANCiAgbGVmdF9qb2luKGNsYXNzZXNfcXVhbGl0ZSwgYnkgPSBjKCJwYXJhbWV0cmVfaWQiKSwgY29weSA9IFRSVUUsIHN1ZmZpeD1jKCIiLCIuY2xhc3NlIikpICU+JQ0KICBmaWx0ZXIocmVzdWx0YXRfaW5kaWNlIDwgYm9ybmVfc3VwX2V4Y2x1ZSAmIHJlc3VsdGF0X2luZGljZSA+PSBib3JuZV9pbmZfaW5jbHVlKQ0KDQp0YWJsZV9pbmRpY2VzX2NsYXNzZXMNCmBgYA0KDQojIEV4cGxvcmF0aW9uIGRlcyBkb25uw6llcw0KDQojIyBEb25uw6llcyBkaXNwb25pYmxlcyBwYXIgYW4gZXQgcGFyIGluZGljZQ0KDQpgYGB7ciBncmFwaGVfaW5kaWNlcywgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTZ9DQp0YWJsZV9pbmRpY2VzX2NsYXNzZXMgJT4lDQogIGdyb3VwX2J5KHllYXIoZGF0ZV9wcmVsZXZlbWVudCksIGxpYmVsbGVfc3VwcG9ydCwgbGliZWxsZV9pbmRpY2UpICU+JQ0KICBzdW1tYXJpc2UoTmJfcmVzdWx0YXRzID0gbigpLA0KICAgICAgICAgICAgQW5uZWUgPSB5ZWFyKGRhdGVfcHJlbGV2ZW1lbnQpKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gYXMuZmFjdG9yKEFubmVlKSwgeT1OYl9yZXN1bHRhdHMsIGZpbGw9bGliZWxsZV9pbmRpY2UpKSsNCiAgICAgICAgICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSsNCiAgZmFjZXRfZ3JpZChsaWJlbGxlX3N1cHBvcnR+LikNCiAgDQpgYGANCg0KIyMgRGlzdHJpYnV0aW9uIGRlcyByw6lzdWx0YXRzIHBhciBpbmRpY2UgZXQgcGFyIGNsYXNzZSBkZSBxdWFsaXTDqQ0KDQpgYGB7ciBncmFwaGVfaW5kaWNlc19jbGFzc2VzLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xNn0NCnRhYmxlX2luZGljZXNfY2xhc3NlcyAlPiUNCiAgYXJyYW5nZShjb2RlLngpJT4lDQogIGdncGxvdChhZXMoeCA9IGNvZGUueCwgeT1yZXN1bHRhdF9pbmRpY2UsIGZpbGwgPSBjb2RlLngpKSsNCiAgICAgICAgICAgZ2VvbV9ib3hwbG90KCkrDQogIGZhY2V0X3dyYXAofmxpYmVsbGVfaW5kaWNlLCBzY2FsZXMgPSAiZnJlZSIpDQogIA0KdGFibGVfaW5kaWNlc19jbGFzc2VzDQpgYGANCg0KIyBUcmFuc2Zvcm1hdGlvbiBkZXMgZG9ubsOpZXMNCg0KIyMgRG9ubsOpZXMgYW5udWVsbGVzDQoNCkRvbm7DqWVzIGFnZ3LDqWfDqWVzIHBhciBhbm7DqWUgZCdhbmFseXNlIDoNCg0KLSBJbmRpY2VzIG1veWVucw0KLSBDbGFzc2VzIG1heGltYWxlcw0KDQpgYGB7ciB0YWJsZV9pbmRpY2VzX2NsYXNzZXNfYW5uZWV9DQoNCnRhYmxlX2luZGljZXNfY2xhc3Nlc19hbm5lZSA8LSB0YWJsZV9pbmRpY2VzX2NsYXNzZXMgJT4lDQogIG11dGF0ZShBbm5lZSA9IHllYXIoYXMuRGF0ZShkYXRlX3ByZWxldmVtZW50KSkpICU+JQ0KICBncm91cF9ieShjb2RlX3N0YXRpb25faHlkcm9iaW8sDQogICAgICAgICAgIGxvbmdpdHVkZV93Z3M4NCwNCiAgICAgICAgICAgbGF0aXR1ZGVfd2dzODQsDQogICAgICAgICAgIGxpYmVsbGVfc3VwcG9ydCwNCiAgICAgICAgICAgY29kZV9pbmRpY2UsDQogICAgICAgICAgIHBhcmFtZXRyZV9pZCwNCiAgICAgICAgICAgbGliZWxsZSwNCiAgICAgICAgICAgc3ltYm9sZSwNCiAgICAgICAgICAgQW5uZWUpICU+JQ0KICBzdW1tYXJpc2UoY2xhc3NlID0gYXMuaW50ZWdlcihtYXgoY29kZS54KSksIA0KICAgICAgICAgICAgcmVzdWx0YXRfaW5kaWNlID0gbWVhbihyZXN1bHRhdF9pbmRpY2UpLA0KICAgICAgICAgICAgcmVzdWx0YXRfcXVhbGlmaWNhdGlvbiA9IGFzLmludGVnZXIobWF4KGNvZGVfcXVhbGlmaWNhdGlvbikpKQ0KDQp0YWJsZV9pbmRpY2VzX2NsYXNzZXNfYW5uZWUNCmBgYA0KDQojIyBDbGFzc2UgZGUgcXVhbGl0w6kgYmlvbG9naXF1ZSBnbG9iYWxlIA0KDQpDYWxjdWwgZCd1bmUgY2xhc3NlIGRlICpxdWFsaXTDqSBiaW9sb2dpcXVlIGdsb2JhbGUqIChtYXhpbXVtIGRlcyBjbGFzc2VzIGRlcyBpbmRpY2VzIGFubnVlbHMpDQoNCmBgYHtyfQ0KdGFibGVfaW5kaWNlc19jbGFzc2VfZ2xvYmFsZV9hbm5lZSA8LSB0YWJsZV9pbmRpY2VzX2NsYXNzZXNfYW5uZWUgJT4lDQogIGdyb3VwX2J5KGNvZGVfc3RhdGlvbl9oeWRyb2JpbywgbG9uZ2l0dWRlX3dnczg0LCBsYXRpdHVkZV93Z3M4NCwgQW5uZWUpJT4lDQogIHN1bW1hcmlzZShSZXN1bHRhdCA9IG1heChjbGFzc2UpKSU+JQ0KICBtdXRhdGUoU2VyaWUgPSAnQ2xhc3NlIC0gUXVhbGl0w6kgYmlvbG9naXF1ZSBHbG9iYWxlJywgDQogICAgICAgICBsaWJlbGxlX3N1cHBvcnQgPSAnUXVhbGl0w6kgYmlvbG9naXF1ZSBHbG9iYWxlJywNCiAgICAgICAgIHN5bWJvbGUgPSAnWCcpDQoNCnRhYmxlX2luZGljZXNfY2xhc3NlX2dsb2JhbGVfYW5uZWUNCmBgYA0KDQojIEltcG9ydCBkZXMgZG9ubsOpZXMgZW4gYmFzZQ0KDQojIyBEb25uw6llcyBhdSBmb3JtYXQgZGUgbGEgdGFibGUgZWF1X3N0cnVjdHVyZS5hbmFseXNlX2Jpb19lc3UgLSBzZXJ2ZXVyIFBvc3RncmVTUUwgREVWDQoNCiMjIyBNaXNlIGVuIGZvcm1lIGRlIGxhIHRhYmxlDQoNCmBgYHtyIGFuYWx5c2VfYmlvX2VzdX0NCg0KYW5hbHlzZV9iaW9fZXN1IDwtIHRhYmxlX2luZGljZXMgJT4lDQogIGZpbHRlcighaXMubmEocmVzdWx0YXRfaW5kaWNlKSklPiUNCiAgbGVmdF9qb2luKHNlbGVjdCh0YmwoY29uX3Bvc3RncmVzcWxfZGV2LCBkYnBseXI6OmluX3NjaGVtYSgiZWF1X3N0cnVjdHVyZSIsImRhdGUiKSksIGRhdGVfaWQsIGRhdGVfZHVfam91ciksIGJ5PWMoImRhdGVfcHJlbGV2ZW1lbnQiID0gImRhdGVfZHVfam91ciIpLCBjb3B5PVRSVUUpJT4lDQogIGxlZnRfam9pbihzZWxlY3QodGJsKGNvbl9wb3N0Z3Jlc3FsX2RldiwgZGJwbHlyOjppbl9zY2hlbWEoImVhdV9zdHJ1Y3R1cmUiLCJzdXBwb3J0IikpLCBzdXBwb3J0X2lkLCBjb2RlKSwgYnk9YygiY29kZV9zdXBwb3J0IiA9ICJjb2RlIiksIGNvcHk9VFJVRSklPiUNCiAgbGVmdF9qb2luKHNlbGVjdCh0YmwoY29uX3Bvc3RncmVzcWxfZGV2LCBkYnBseXI6OmluX3NjaGVtYSgiZWF1X3N0cnVjdHVyZSIsInJlbWFycXVlIikpLCByZW1hcnF1ZV9pZCwgY29kZSksIGJ5PWMoImNvZGVfcXVhbGlmaWNhdGlvbiIgPSAiY29kZSIpLCBjb3B5PVRSVUUpJT4lDQogIG11dGF0ZShwcmVsZXZlbWVudF9jb2RlID0gcGFzdGUwKGNvZGVfc3RhdGlvbl9oeWRyb2JpbyxkYXRlX2lkKSwNCiAgICAgICAgIHJkZF9pZCA9IDAsDQogICAgICAgICBtaWxpZXVfaWQgPSAzLA0KICAgICAgICAgZnJhY3Rpb25faWQgPSAyMiwNCiAgICAgICAgIGxpbWl0ZV9xdWFudGlmaWNhdGlvbiA9IDAsDQogICAgICAgICBzb3VyY2UgPSAnT0ZCL05BSUFERVMnLA0KICAgICAgICAgbWFqID0gZm9ybWF0KFN5cy5EYXRlKCksIiVZLSVtLSVkIikpJT4lDQogIHNlbGVjdChzaXRlX2lkLA0KICAgICAgICAgZGF0ZV9pZCwNCiAgICAgICAgIHJkZF9pZCwNCiAgICAgICAgIHByZWxldmVtZW50X2NvZGUsDQogICAgICAgICBtaWxpZXVfaWQsDQogICAgICAgICBzdXBwb3J0X2lkLA0KICAgICAgICAgZnJhY3Rpb25faWQsDQogICAgICAgICBwYXJhbWV0cmVfaWQsDQogICAgICAgICByZXN1bHRhdCA9IHJlc3VsdGF0X2luZGljZSwNCiAgICAgICAgIHJlbWFycXVlX2lkLA0KICAgICAgICAgbGltaXRlX3F1YW50aWZpY2F0aW9uLA0KICAgICAgICAgc291cmNlLA0KICAgICAgICAgbWFqKQ0KDQphbmFseXNlX2Jpb19lc3UNCmBgYA0KDQojIyMgU8OpbGVjdGlvbiBkZXMgbm91dmVsbGVzIGxpZ25lcyDDoCBpbnPDqXJlcg0KDQpgYGB7ciBpbnNlcnRfYW5hbHlzZV9iaW9fZXN1fQ0KIyBUT0RPIGNvbXBhcmFpc29uIGF2ZWMgbGEgYmFzZQ0KaW5zZXJ0X2FuYWx5c2VfYmlvX2VzdSA8LSBhbmFseXNlX2Jpb19lc3UNCg0KYGBgDQoNCiMjIyBJbnNlcnRpb24gZGVzIG5vdXZlbGxlcyBsaWduZXMNCg0KYGBge3IgaW5zZXJ0IHJlc3VsdGF0c19pbmNvbm51cywgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCmRiRXhlY3V0ZShjb25fcG9zdGdyZXNxbF9kZXYsICJUUlVOQ0FURSBUQUJMRSBlYXVfc3RydWN0dXJlLmFuYWx5c2VfYmlvX2VzdSIpDQoNCmRiQXBwZW5kVGFibGUoY29ubiA9IGNvbl9wb3N0Z3Jlc3FsX2RldiwgbmFtZSA9IElkKHNjaGVtYSA9ICJlYXVfc3RydWN0dXJlIix0YWJsZSA9ICJhbmFseXNlX2Jpb19lc3UiKSwgdmFsdWUgPSBpbnNlcnRfYW5hbHlzZV9iaW9fZXN1LCBmaWxlRW5jb2Rpbmc9ImxhdGluMSIpDQpgYGANCg0KIyMgRG9ubsOpZXMgYXUgZm9ybWF0IGRlIGxhIHRhYmxlIGVhdV90Ymkub2ViX2VhdV9xdWFsaXRlX2Jpb2xvZ2lxdWVfY2UgLSBzZXJ2ZXVyIE1hcmlhREIgT0VCDQoNCmBgYHtyIHNlcmllc19pbmRpY2VzX2FubnVlbHN9DQoNCnRhYmxlX3Nlcmllc19pbmRpY2VzIDwtIHRhYmxlX2luZGljZXNfY2xhc3Nlc19hbm5lZSAlPiUgDQogIG11dGF0ZShpbmRpY2UgPSBwYXN0ZShsaWJlbGxlLCBjb2RlX2luZGljZSwgc2VwPScgLSAnKSklPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHM9IGMoImNsYXNzZSIsInJlc3VsdGF0X2luZGljZSIsInJlc3VsdGF0X3F1YWxpZmljYXRpb24iKSklPiUNCiAgbXV0YXRlKFNlcmllID0gY2FzZV93aGVuKG5hbWUgPT0gJ2NsYXNzZScgfiBwYXN0ZSgnQ2xhc3NlJyxsaWJlbGxlX3N1cHBvcnQsc2VwID0gJyAtICcpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9PSAncmVzdWx0YXRfaW5kaWNlJyB+IHBhc3RlKCdJbmRpY2UnLGluZGljZSxzZXAgPSAnIC0gJyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID09ICdyZXN1bHRhdF9xdWFsaWZpY2F0aW9uJyB+IHBhc3RlKCdRdWFsaWZpY2F0aW9uJyxpbmRpY2Usc2VwID0gJyAtICcpDQogICAgICAgICAgICAgICAgICAgICAgICAgICApDQogICAgICAgICApJT4lDQogIGdyb3VwX2J5KGNvZGVfc3RhdGlvbl9oeWRyb2JpbywNCiAgICAgICAgICAgbG9uZ2l0dWRlX3dnczg0LA0KICAgICAgICAgICBsYXRpdHVkZV93Z3M4NCwNCiAgICAgICAgICAgbGliZWxsZV9zdXBwb3J0LA0KICAgICAgICAgICBTZXJpZSwNCiAgICAgICAgICAgc3ltYm9sZSwNCiAgICAgICAgICAgQW5uZWUpJT4lDQogIHN1bW1hcmlzZShSZXN1bHRhdCA9IG1heCh2YWx1ZSkpJT4lDQogIHVuZ3JvdXAoKSU+JQ0KICB1bmlvbih0YWJsZV9pbmRpY2VzX2NsYXNzZV9nbG9iYWxlX2FubmVlKQ0KDQp0YWJsZV9zZXJpZXNfaW5kaWNlcw0KYGBgDQojIyMgRMOpY2xpbmFpc29uIHBhciBjb21iaW5haXNvbiBTSVRFIC8gRUdBDQoNCkxhIHRhYmxlIGRlIHZhbG9yaXNhdGlvbiBkw6ljbGluZSBsZXMgcsOpc3VsdGF0cyBwb3VyIGNoYXF1ZSDDqWNoZWxsZSBkZSB0ZXJyaXRvaXJlDQoNCmBgYHtyIHRhYmxlX3Nlcmllc19pbmRpY2VzX2VnYX0NCnRhYmxlX3Nlcmllc19pbmRpY2VzX2VnYSA8LSB0YWJsZV9zZXJpZXNfaW5kaWNlcyU+JQ0KICAjIHRhYmxlIGRlcyBjb3JyZXNwb25kYW5jZXMgc2l0ZXMgLyBVR0ENCiAgbGVmdF9qb2luKGNvcnJlc3BvbmRhbmNlX3NpdGVfZWdhLCBieT1jKCJjb2RlX3N0YXRpb25faHlkcm9iaW8iID0gImNkc2l0ZSIpLCBjb3B5ID0gVFJVRSklPiUNCiAgbGVmdF9qb2luKHNpdGVzX3JjcywgYnk9ImNvZGVfc3RhdGlvbl9oeWRyb2JpbyIpDQoNCnRhYmxlX3Nlcmllc19pbmRpY2VzX2VnYQ0KYGBgDQojIyMgTWlzZSBlbiBmb3JtZSBkZSBsYSB0YWJsZQ0KDQpgYGB7ciB0YWJsZSBvZWJfZWF1X3F1YWxpdGVfYmlvbG9naXF1ZV9jZX0NCg0Kb2ViX2VhdV9xdWFsaXRlX2Jpb2xvZ2lxdWVfY2UgPC0gdGFibGVfc2VyaWVzX2luZGljZXNfZWdhICU+JQ0KICBtdXRhdGUoUGVyaW9kZSA9IGFzLmNoYXJhY3RlcihBbm5lZSksDQogICAgICAgICBTb3VyY2UgPSAnT0ZCL05BSUFERVMnLA0KICAgICAgICAgTWlzZV9hX2pvdXIgPSBmb3JtYXQoU3lzLkRhdGUoKSwiJVktJW0tJWQiKQ0KICAgICAgICAgKSU+JSANCiAgc2VsZWN0KFR5cGVfZW50aXRpdGVfZ2VvZ3JhcGhpcXVlID0gdHlwZXNpdGUsDQogICAgICAgICBDb2RlX2VudGl0aXRlX2dlb2dyYXBoaXF1ZSA9IGNvZGVfc3RhdGlvbl9oeWRyb2JpbywNCiAgICAgICAgIExpYmVsbGVfZW50aXRpdGVfZ2VvZ3JhcGhpcXVlID0gbGJzaXRlLA0KICAgICAgICAgQ29vcmRYX1dHUzg0ID0gbG9uZ2l0dWRlX3dnczg0LA0KICAgICAgICAgQ29vcmRZX1dHUzg0ID0gbGF0aXR1ZGVfd2dzODQsDQogICAgICAgICBSZXNlYXVfUkNTID0gaW5jbHVzX3JjcywNCiAgICAgICAgIFR5cGVfZW50aXRpdGVfZ2VvZ3JhcGhpcXVlX2Fzc29jaWVlID0gdHlwZWVnYSwNCiAgICAgICAgIENvZGVfZW50aXRpdGVfZ2VvZ3JhcGhpcXVlX2Fzc29jaWVlID0gY2RlZ2EsDQogICAgICAgICBMaWJlbGxlX2VudGl0aXRlX2dlb2dyYXBoaXF1ZV9hc3NvY2llZSA9IGxiZWdhLA0KICAgICAgICAgUGVyaW9kZSwNCiAgICAgICAgIFNlcmllLA0KICAgICAgICAgdW5pdGUgPSBzeW1ib2xlLA0KICAgICAgICAgUmVzdWx0YXQsDQogICAgICAgICBTb3VyY2UsDQogICAgICAgICBNaXNlX2Ffam91cg0KICApDQoNCm9lYl9lYXVfcXVhbGl0ZV9iaW9sb2dpcXVlX2NlDQoNCmBgYA0KIyMjIFPDqWxlY3Rpb24gZGVzIGxpZ25lcyBub3V2ZWxsZXMgw6AgaW5zw6lyZXINCg0KTGEgdGFibGUgZGUgcsOpc3VsdGF0cyBlc3QgY29tcGFyw6llIGF1eCBkb25uw6llcyBkw6lqw6AgYmFuY2FyaXPDqWVzIHBhciB1bmUgam9pbnR1cmUgZCdleGNsdXNpb24gKGFudGlfam9pbikuIExlcyBsaWduZXMgcmVzdGFudGVzIHBldXZlbnQgw6p0cmUgaW5zw6lyw6llcy4NCg0KYGBge3IgdGFibGUgaW5zZXJ0X29lYl9lYXVfcXVhbGl0ZV9iaW9sb2dpcXVlX2NlfQ0KDQppbnNlcnRfb2ViX2VhdV9xdWFsaXRlX2Jpb2xvZ2lxdWVfY2UgPC0gb2ViX2VhdV9xdWFsaXRlX2Jpb2xvZ2lxdWVfY2UgJT4lIA0KICB1bmdyb3VwKCkgJT4lDQogICMgUmV0aXJlciBsZXMgbGlnbmVzIGRlcyByw6lzdWx0YXRzIGTDqWrDoCBleGlzdGFudHMgZGFucyBsYSB0YWJsZQ0KICBhbnRpX2pvaW4odGJsKGNvbl9lYXVfdGJpLCJvZWJfZWF1X3F1YWxpdGVfYmlvbG9naXF1ZV9jZSIsIGJ5PWMoIlR5cGVfZW50aXRpdGVfZ2VvZ3JhcGhpcXVlIiwgIkNvZGVfZW50aXRpdGVfZ2VvZ3JhcGhpcXVlIiwgIlR5cGVfZW50aXRpdGVfZ2VvZ3JhcGhpcXVlX2Fzc29jaWVlIiwgIkNvZGVfZW50aXRpdGVfZ2VvZ3JhcGhpcXVlX2Fzc29jaWVlIiwgIlBlcmlvZGUiLCAiU2VyaWUiKSksIGNvcHkgPSBUUlVFKSU+JQ0KICAjIFJldGlyZXIgbGVzIHN0YXRpb25zIGh5ZHJvIHNhbnMgRUdBIGlkZW50aWZpw6llDQogIGZpbHRlcighaXMubmEoVHlwZV9lbnRpdGl0ZV9nZW9ncmFwaGlxdWUpKSU+JQ0KICAjIFJldGlyZXIgbGVzIGxpZ25lcyBhdmVjIHVuZSB2YWxldXIgTkENCiAgZHJvcF9uYSgpDQoNCmluc2VydF9vZWJfZWF1X3F1YWxpdGVfYmlvbG9naXF1ZV9jZQ0KYGBgDQoNCiMjIyBJbnNlcnRpb24gZGVzIG5vdXZlbGxlcyBsaWduZXMNCg0KYGBge3IgaW5zZXJ0IG9lYl9lYXVfcXVhbGl0ZV9iaW9sb2dpcXVlX2NlLCBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KZGJFeGVjdXRlKGNvbl9lYXVfdGJpLCAiVFJVTkNBVEUgVEFCTEUgb2ViX2VhdV9xdWFsaXRlX2Jpb2xvZ2lxdWVfY2UiKQ0KDQpkYkFwcGVuZFRhYmxlKGNvbm4gPSBjb25fZWF1X3RiaSwgbmFtZSA9ICJvZWJfZWF1X3F1YWxpdGVfYmlvbG9naXF1ZV9jZSIsIHZhbHVlID0gaW5zZXJ0X29lYl9lYXVfcXVhbGl0ZV9iaW9sb2dpcXVlX2NlLCBmaWxlRW5jb2Rpbmc9ImxhdGluMSIpDQpgYGANCg0KIyMgRXhwb3J0cyBDU1YgcG91ciBsYSBwdWJsaWNhdGlvbiBkdSBqZXUgZGUgZG9ubsOpZXMgc3VyIGxlcyBwb3J0YWlscyBvcGVuZGF0YQ0KDQojIyMgRXhwb3J0IHBvdXIgbGUgR0lERQ0KDQpGaWNoaWVyIHBvdXIgbGEgcHVibGljYXRpb24gZHUgamV1IGRlIGRvbm7DqWVzIHN1ciBsZSBwb3J0YWlsIE9FQg0KDQpgYGB7ciBleHBvcnQgb2ViX2VhdV9xdWFsaXRlX2Jpb2xvZ2lxdWVfY2V9DQojIERlcHVpcyBsYSBiYXNlIGRlIGRvbm7DqWVzDQojdGJsKGNvbl9lYXVfdGJpLCAib2ViX2VhdV9xdWFsaXRlX2Jpb2xvZ2lxdWVfY2VfbmV3IiklPiUNCiMgb3UgZGVwdWlzIGxhIHRhYmxlIGVuIG3DqW1vaXJlDQpvZWJfZWF1X3F1YWxpdGVfYmlvbG9naXF1ZV9jZSAlPiUgIA0Kd3JpdGUudGFibGUoZmlsZSA9IHBhc3RlMChwYXJhbXMkcGF0aF9kYXRhdml6LCJcXEdJREVcXCIsIm9lYl9lYXVfcXVhbGl0ZV9iaW9sb2dpcXVlX2NlLmNzdiIpLCBxdW90ZSA9IFRSVUUsIHNlcCA9ICI7IiwNCiAgICAgICAgICAgIGVvbCA9ICJcbiIsIG5hID0gIiIsIGRlYyA9ICIsIiwNCiAgICAgICAgICAgIGZpbGVFbmNvZGluZyA9ICJVVEYtOCIpDQpgYGANCg0KIyMjIEV4cG9ydCBwb3VyIEdFT0INCg0KRmljaGllciBwb3VyIGxhIHB1YmxpY2F0aW9uIGR1IGpldSBkZSBkb25uw6llcyBzdXIgR2VvYnJldGFnbmUNCg0KIyMjIyBNaXNlIGVuIGZvcm1lIGRlIGxhIHRhYmxlIHBvdXIgbCdlbnNlbWJsZSBkZXMgcGFyYW3DqHRyZXMNCg0KYGBge3Igb2ViX2VhdV9xdWFsaXRlX2dlb2J9DQpvZWJfZWF1X3F1YWxpdGVfZ2VvYiA8LSB0YWJsZV9zZXJpZXNfaW5kaWNlcyAgJT4lDQogIHVuZ3JvdXAoKSU+JQ0KICBsZWZ0X2pvaW4oc2VsZWN0KHNpdGVzX3NhZ2VzLGNvZGVfc3RhdGlvbl9oeWRyb2JpbyxsaWJlbGxlX3N0YXRpb25faHlkcm9iaW8pLCBieT0iY29kZV9zdGF0aW9uX2h5ZHJvYmlvIikgICU+JQ0KICBtdXRhdGUoQ29kZV9lbnRpdGl0ZV9nZW9ncmFwaGlxdWUgPSBjb2RlX3N0YXRpb25faHlkcm9iaW8sDQogICAgICAgICBMaWJlbGxlX2VudGl0aXRlX2dlb2dyYXBoaXF1ZSA9IGxpYmVsbGVfc3RhdGlvbl9oeWRyb2JpbywNCiAgICAgICAgIENvb3JkWF9XR1M4NCA9IGxvbmdpdHVkZV93Z3M4NCwNCiAgICAgICAgIENvb3JkWV9XR1M4NCA9IGxhdGl0dWRlX3dnczg0LA0KICAgICAgICAgU2VyaWUsDQogICAgICAgICB1bml0ZSA9IHN5bWJvbGUsDQogICAgICAgICBUeXBlX2VudGl0aXRlX2dlb2dyYXBoaXF1ZSA9ICdTSVRFJywNCiAgICAgICAgIFNvdXJjZSA9ICdPRkIvTkFJQURFUycsDQogICAgICAgICBNaXNlX2Ffam91ciA9IGZvcm1hdChTeXMuRGF0ZSgpLCIlWS0lbS0lZCIpKSAlPiUNCiAgc2VsZWN0KFR5cGVfZW50aXRpdGVfZ2VvZ3JhcGhpcXVlLA0KICAgICAgICAgQ29kZV9lbnRpdGl0ZV9nZW9ncmFwaGlxdWUsDQogICAgICAgICBMaWJlbGxlX2VudGl0aXRlX2dlb2dyYXBoaXF1ZSwNCiAgICAgICAgIENvb3JkWF9XR1M4NCwNCiAgICAgICAgIENvb3JkWV9XR1M4NCwNCiAgICAgICAgIFNlcmllLA0KICAgICAgICAgdW5pdGUsDQogICAgICAgICBTb3VyY2UsDQogICAgICAgICBNaXNlX2Ffam91ciwNCiAgICAgICAgIEFubmVlLA0KICAgICAgICAgUmVzdWx0YXQpICU+JQ0KICBwaXZvdF93aWRlcih2YWx1ZXNfZnJvbSA9IFJlc3VsdGF0LCBuYW1lc19mcm9tID0gQW5uZWUsIG5hbWVzX3NvcnQ9VFJVRSkNCmBgYA0KDQojIyMjIEV4cG9ydCBwb3VyIGxlIHBhcmFtw6h0cmUgIlF1YWxpdMOpIGdsb2JhbGUiDQoNCg0KYGBge3IgZXhwb3J0IG9lYl9lYXVfcXVhbGl0ZV9iaW9sb2dpcXVlX2dsb2JhbGV9DQoNCm9lYl9lYXVfcXVhbGl0ZV9nZW9iICAlPiUNCiAgZmlsdGVyKFNlcmllID09ICdDbGFzc2UgLSBRdWFsaXTDqSBiaW9sb2dpcXVlIEdsb2JhbGUnKSAgJT4lDQp3cml0ZS50YWJsZShmaWxlID0gcGFzdGUwKHBhcmFtcyRwYXRoX2RhdGF2aXosIlxcR0VPQlxcIiwib2ViX2VhdV9xdWFsaXRlX2Jpb2xvZ2lxdWVfZ2xvYmFsZS5jc3YiKSwgcXVvdGUgPSBUUlVFLCBzZXAgPSAiOyIsDQogICAgICAgICAgICBlb2wgPSAiXG4iLCBuYSA9ICIiLCBkZWMgPSAiLCIsDQogICAgICAgICAgICBmaWxlRW5jb2RpbmcgPSAiVVRGLTgiKQ0KYGBgDQoNCiMjIyMgRXhwb3J0IHBvdXIgbGUgcGFyYW3DqHRyZSAiRGlhdG9tw6llcyINCg0KYGBge3IgZXhwb3J0IG9lYl9lYXVfcXVhbGl0ZV9kaWF0b21lZXN9DQoNCm9lYl9lYXVfcXVhbGl0ZV9nZW9iICU+JQ0KICBmaWx0ZXIoU2VyaWUgPT0gJ0NsYXNzZSAtIERpYXRvbcOpZXMgYmVudGhpcXVlcycpICU+JQ0Kd3JpdGUudGFibGUoZmlsZSA9IHBhc3RlMChwYXJhbXMkcGF0aF9kYXRhdml6LCJcXEdFT0JcXCIsIm9lYl9lYXVfcXVhbGl0ZV9kaWF0b21lZXMuY3N2IiksIHF1b3RlID0gVFJVRSwgc2VwID0gIjsiLA0KICAgICAgICAgICAgZW9sID0gIlxuIiwgbmEgPSAiIiwgZGVjID0gIiwiLA0KICAgICAgICAgICAgZmlsZUVuY29kaW5nID0gIlVURi04IikNCmBgYA0KDQojIyMjIEV4cG9ydCBwb3VyIGxlIHBhcmFtw6h0cmUgIk1hY3JvaW52ZXJ0w6licsOpcyINCg0KYGBge3IgZXhwb3J0IG9lYl9lYXVfcXVhbGl0ZV9tYWNyb2ludmVydGVicmVzfQ0KDQpvZWJfZWF1X3F1YWxpdGVfZ2VvYiAlPiUNCiAgZmlsdGVyKFNlcmllID09ICdDbGFzc2UgLSBNYWNyb2ludmVydMOpYnLDqXMgYXF1YXRpcXVlcycpICU+JQ0Kd3JpdGUudGFibGUoZmlsZSA9IHBhc3RlMChwYXJhbXMkcGF0aF9kYXRhdml6LCJcXEdFT0JcXCIsIm9lYl9lYXVfcXVhbGl0ZV9tYWNyb2ludmVydGVicmVzLmNzdiIpLCBxdW90ZSA9IFRSVUUsIHNlcCA9ICI7IiwNCiAgICAgICAgICAgIGVvbCA9ICJcbiIsIG5hID0gIiIsIGRlYyA9ICIsIiwNCiAgICAgICAgICAgIGZpbGVFbmNvZGluZyA9ICJVVEYtOCIpDQpgYGANCg0KIyMjIyBFeHBvcnQgcG91ciBsZSBwYXJhbcOodHJlICJNYWNyb3BoeXRlcyINCg0KYGBge3IgZXhwb3J0IG9lYl9lYXVfcXVhbGl0ZV9tYWNyb3BoeXRlc30NCg0Kb2ViX2VhdV9xdWFsaXRlX2dlb2IgJT4lDQogIGZpbHRlcihTZXJpZSA9PSAnQ2xhc3NlIC0gTWFjcm9waHl0ZXMnKSAlPiUNCndyaXRlLnRhYmxlKGZpbGUgPSBwYXN0ZTAocGFyYW1zJHBhdGhfZGF0YXZpeiwiXFxHRU9CXFwiLCJvZWJfZWF1X3F1YWxpdGVfbWFjcm9waHl0ZXMuY3N2IiksIHF1b3RlID0gVFJVRSwgc2VwID0gIjsiLA0KICAgICAgICAgICAgZW9sID0gIlxuIiwgbmEgPSAiIiwgZGVjID0gIiwiLA0KICAgICAgICAgICAgZmlsZUVuY29kaW5nID0gIlVURi04IikNCmBgYA==